Dictionary unique values in comprehension - python

I have a little task which I solved.
Task: find all PAIRS in a sequence which sum up to a certain number.
For example (1,2,3,4) and target 3 yields one pair (1,2).
I came up with a solution:
def pair(lst, find):
res = []
for i in lst:
if (find - i) in lst:
res.append([(find - i),i])
return {x:y for x,y in res}
I'm a bit surprised to see the dictionary comprehension filter all duplicate solutions.
Which actually forms my question: how and why a dictionary comprehension removes duplicates?

Because dict hashes its keys then store them in a set-like data structure. As a result the newly created {key :value} overrides the older one and in your case the duplicates. I think this may be a duplicate question

Related

Creating list from list of dictionaries

I'm tasked with creating a list of the values associated with a specific key from a list of dictionaries.
Currently, I'm two for loops and a conditional into this but I know there's a much more efficient way of going about this.
#lst = list of dict.
#k = specific key being checked for
for dict in lst:
for ind in dict:
if dict[ind] == k:
ret_val.append(dict[ind])
The instructions also state that I am to assume that all dictionaries contain the key and that the value should only be added if the value doesn't already exist.
You might be looking for this:
ret_val = [dict[ind] for dict in lst for ind in dict if dict[ind]==k]

Python pick a random value from hashmap that has a list as value?

so I have a defaultdict(list) hashmap, potential_terms
potential_terms={9: ['leather'], 10: ['type', 'polyester'], 13:['hello','bye']}
What I want to output is the 2 values (words) with the lowest keys, so 'leather' is definitely the first output, but 'type' and 'polyester' both have k=10, when the key is the same, I want a random choice either 'type' or 'polyester'
What I did is:
out=[v for k,v in sorted(potential_terms.items(), key=lambda x:(x[0],random.choice(x[1])))][:2]
but when I print out I get :
[['leather'], ['type', 'polyester']]
My guess is ofcourse the 2nd part of the lambda function: random.choice(x[1]). Any ideas on how to make it work as expected by outputting either 'type' or 'polyester' ?
Thanks
EDIT: See Karl's answer and comment as to why this solution isn't correct for OP's problem.
I leave it here because it does demonstrate what OP originally got wrong.
key= doesn't transform the data itself, it only tells sorted how to sort,
you want to apply choice on v when selecting it for the comprehension, like so:
out=[random.choice(v) for k,v in sorted(potential_terms.items())[:2]]
(I also moved the [:2] inside, to shorten the list before the comprehension)
Output:
['leather', 'type']
OR
['leather', 'polyester']
You have (with some extra formatting to highlight the structure):
out = [
v
for k, v in sorted(
potential_terms.items(),
key=lambda x:(x[0], random.choice(x[1]))
)
][:2]
This means (reading from the inside out): sort the items according to the key, breaking ties using a random choice from the value list. Extract the values (which are lists) from those sorted items into a list (of lists). Finally, get the first two items of that list of lists.
This doesn't match the problem description, and is also somewhat nonsensical: since the keys are, well, keys, there cannot be duplicates, and thus there cannot be ties to break.
What we wanted: sort the items according to the key, then put all the contents of those individual lists next to each other to make a flattened list of strings, but randomizing the order within each sublist (i.e., shuffling those sublists). Then, get the first two items of that list of strings.
Thus, applying the technique from the link, and shuffling the sublists "inline" as they are discovered by the comprehension:
out = [
term
for k, v in sorted(
potential_terms.items(),
key = lambda x:x[0] # this is not actually necessary now,
# since the natural sort order of the items will work.
)
for term in random.sample(v, len(v))
][:2]
Please also see https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/ to understand how the list flattening and result ordering works in a two-level comprehension like this.
Instead of the out, a simpler function, is:
d = list(p.values()) which stores all the values.
It will store the values as:
[['leather'], ['polyester', 'type'], ['hello', 'bye']]
You can access, leather as d[0] and the list, ['polyester', 'type'], as d[1]. Now we'll just use random.shuffle(d[1]), and use d[1][0].
Which would get us a random word, type or polyester.
Final code should be like this:
import random
potential_terms={9: ['leather'], 10: ['type', 'polyester'], 13:['hello','bye']}
d = list(p.values())
random.shuffle(d[1])
c = []
c.append(d[0][0])
c.append(d[1][0])
Which gives the desired output,
either ['leather', 'polyester'] or ['leather', 'type'].

How to only copy values from one dictionary to another?

I have two dictionaries with multiple values:
t={'Musée de Armée':130102845,48.8570374,2.3118779}
While the other s is the sorted Ordered Dictionary of t:
s={'193 Gallery':3359610327,48.8624495,2.3652262}
Both dictionaries have a length of 800 and I would like to merge both dictionaries while keeping their values such that the final result is:
t={'Musée de Armée':130102845,48.8570374,2.3118779,'193 Gallery',3359610327,48.8624495,2.3652262}
This is what I have tried:
s = OrderedDict(sorted(t.items()))
for k,v in t,s:
t[k].append(s.values())
It gives me an error regarding too many values to unpack.
So I would like to join sorted and unsorted dictionary into one.
In general, you can create a new OrderedDict from two other like that:
my_od = OrderedDict(**s, **t)
You can try:
from collections import defaultdict
u = defaultdict(list)
for k, v in in s, t:
u[k].extend(v)
To those answering my question and providing guidance, thanks for your help. I had come to a solution the following day after posting this question. I just hadn't had the time to post it.What worked for me however, was to iterate through both the dictionary items in an array-like manner and the zip method:
zip(*iterables)
Make an iterator that aggregates elements from each of the iterables. Returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables. The iterator stops when the shortest input iterable is exhausted. With a single iterable argument, it returns an iterator of 1-tuples. With no arguments, it returns an empty iterator.The left-to-right evaluation order of the iterables is guaranteed. This makes possible an idiom for clustering a data series into n-length groups using zip(*[iter(s)]*n). This repeats the same iterator n times so that each output tuple has the result of n calls to the iterator. This has the effect of dividing the input into n-length chunks.zip() should only be used with unequal length inputs when you don’t care about trailing, unmatched values from the longer iterables. If those values are important, use itertools.zip_longest() instead.zip() in conjunction with the * operator can be used to unzip a list
This was what I did:
d = dict()
for (k, v), (k2, v2) in zip(t.items(), s.items()):
d[k] = [v[0], v[1], v[2], k2, v2[0], v2[1], v2[2]]
zip is indeed a good tool when you have paired elements, as in your case. Since you did not say in your question that there are always exactly three numbers in the values, here is a more general solution that also modifies t in place, as you intended:
for key, (newkey, newval) in zip (t, s.items()):
t[key].extend([ newkey ] + newval)
This appends the key and values from s to the entry in t. Or you could do it in two steps, it's a matter of taste:
for key, (newkey, newval) in zip (t, s.items()):
t[key].append(newkey)
t[key].extend(newval)

sort and group dict of list if list contains others

I have a dict of lists
dict = {k1:[v1],k2:[v1,v2,v3,v4,v5],k3:[v2,v3],k4[v1,v2]}
i want to sort dict as the next elements have previous elements e.g.
OrderedDict = ((k1,[v1]),(k4,[v1,v2]),(k3,[v2,v3]),(k2,[v1,v2,v3,v4,v5]))
and sorted based on their priority:
SortedGroup = [1:((k1,[v1])),2:((k4,[v1,v2]),(k3,[v2,v3])),3:(k2,[v1,v2,v3,v4,v5]))]
i have tried sorted(dict.items(),key = lambda x: len(x[1])) but it's based on len(list) only not list elements. Thought about permutation but have not seen many examples with them.
Please check the official documentation first, there is actually an example for that here.
OrderedDict(sorted(d.items(), key=lambda t: t1))

Dedupe python list based on multiple criteria

I have a list:
mylist = [('Item A','CA','10'),('Item B','CT','12'),('Item A','CA','14'),('Item A','NH','10')]
I would like to remove duplicates based on column 1 and 2. So my desired output would be:
[('Item A','CA','10'),('Item B','CT','12'),('Item A','NH','10')]
I'm not really sure how to go about this, so I haven't posted any code, but am just looking for some help :)
Use a dict. The other answer is good. For variety, here's a single expression that will give you the uniq'd list (though the order of elements is not preserved).
{ tuple(item[0:2]):item for item in mylist[::-1] }.values()
This creates a dict from the elements of mylist using elements 0 and 1 as the key (implicitly removing duplicates). Because mylist is iterated in reverse order, the last element with a duplicate key (elements 0 and 1) will remain in the dict.
Dict keys can be of any hashable type. Create a dict with the first two columns of each item as the key, and only add to unique if those columns haven't been seen before.
unique = {}
for item in mylist:
if item[0:2] not in unique:
unique[item[0:2]] = item
print unique.values()

Categories