Python : What's wrong with the dictionary comprehension? - python

My Code :
dict( (k,v) if k in ['1','2','3','4'] else (k,None) for k,v in {'1':'one','2':'two'}.items() )
Expected Output :
{'1': 'one', '2': 'two', '3':None, '4':None}
Actual Output :
{'1': 'one', '2': 'two'}
Please help !

The for clause only contains 2 items, so your final dict is only going to have 2 items. The 4-item list is only used as a comparison, not as a source of values to use.

if k in ['1','2','3','4']
is a condition, not an iteration (see the if).
You can write it as:
dict((k, {'1':'one','2':'two'}.get(k, None)) for k in ['1','2','3','4'])
# returns: {'1': 'one', '2': 'two', '3': None, '4': None}

This would give the required result, as you need to traverse the list for 4 elements, and then assign respective values in the dictionary :
theDict = {'1':'one','2':'two'}
s = dict( (k,theDict[k]) if k in theDict else (k,None) for k in ['1','2','3','4'] )

u iterate over a dict with two keys, not a 4-element list
my code:
dict( (k, {'1':'one', '2':'two'}.get(k, None)) for k in ['1', '2','3', '4'])

If my crystal ball wasn't broken, I would guess you want the other direction: iterate k over the list and check if it is in the dictionary.
In this case, you might find useful
l = ['1', '2', '3', '4']
testdict = {'1': 'one', '2': 'two'}
res = dict((k, testdict.get(k, None)) for k in l)
This iterates over l and yields a (k, testdict[k]) tuple if k is in the testdict.
If not, it yields (k, None) - exactly as you (supposedly!) want.

Related

In python, create index from flat representation of nested structure in a list, sorting by alphabetical order

I have lists where each entry is representing a nested structure, where / represents each level in the structure.
['a','a/b/a','a/b','a/b/d',....]
I want to take such a list and return an index list where each level is sorted in alphabetical order.
If we had the following list
['a','a/b','a/b/a','a/c','a/c/a','b']
It represents the nested structure
'a': #1
'b': #1.1
'a': ... #1.1.1
'c': #1.2
'a': ... #1.2.1
'b' : ... #2
I am trying to get the output
['1','1.1','1.1.1', '1.2','1.2.1','2']
But I am having real issue on how to tackle the problem, would it be solved recursively? Or what would be a way to solve this for any generic list where each level is separated by /? The list is originally not necessarily sorted, and each level can be any generic word.
Here's what I have tried:
from operator import itemgetter
from functools import reduce
lst = ['a','a/b','a/b/a','a/c','a/c/a','b']
# build a dict
dct = {}
for keys in lst:
reduce(lambda d, k: d.setdefault(k, {}), keys.split('/'), dct)
print(dct) # {'a': {'b': {'a': {}}, 'c': {'a': {}}}, 'b': {}}
def read_dct(dct, prefix=None):
if not dct: # empty dict, i.e., at a leaf
return
sorted_items = sorted(dct.items(), key=itemgetter(0)) # sort on keys
for i, (_, v) in enumerate(sorted_items, start=1):
yield (current := f"{prefix}.{i}" if prefix else str(i))
yield from read_dct(v, current)
print([*read_dct(dct)]) # ['1', '1.1', '1.1.1', '1.2', '1.2.1', '2']
Basically, the first part builds a dictionary to represent the tree structure. And then I use a recursive function to make a list from the dict.
Here's a similar solution to the accepted answer, but I think it might be more correct than that answer. If I understand the problem correctly, there should be exactly one value in the output list for each value in the input list. A input of ['a/b/c/d'] should result in ['1.1.1.1'], not in a list with four values.
Anyway, here's my solution, with a couple of extra test cases:
def doit(inp):
def recursive_print(struct, sum=""):
if sum and struct[1]:
print(sum)
for i, key in enumerate(sorted(struct[0].keys())):
recursive_print(struct[0][key], sum + ("." if sum else "") + str(i + 1))
struct = [{}, False]
for v in inp:
p = last = struct
for part in v.split('/'):
if part not in p[0]:
p[0][part] = [{}, False]
p = p[0][part]
p[1] = True
recursive_print(struct)
inp = ['a','a/b','a/b/a','a/c','a/c/a','b']
doit(inp)
print()
inp = ['a/b/c/d']
doit(inp)
print()
inp = ['joe','joe/sam','larry/curly/moe','jerry/jill','jerry/jill/tom','jerry/jill/tony','alice/jill/betty/davis/eyes','john']
doit(inp)
Result:
1
1.1
1.1.1
1.2
1.2.1
2
1.1.1.1
1.1.1.1.1
2.1
2.1.1
2.1.2
3
3.1
4
5.1.1
Since the goal is to simply convert the paths to indices according to their respective positions against other paths of the same prefix, there is no need to build a tree at all. Instead, iterate over the paths in alphabetical order while using a dict of sets to keep track of the prefixes at each level of paths, and join the lengths of sets at each level for output:
def indices(paths):
output = {}
names = {}
for index, path in sorted(enumerate(paths), key=lambda t: t[1]):
counts = []
prefixes = tuple(path.split('/'))
for level, name in enumerate(prefixes):
prefix = prefixes[:level]
names.setdefault(prefix, set()).add(name)
counts.append(len(names[prefix]))
output[index] = '.'.join(map(str, counts))
return list(map(output.get, range(len(output))))
so that:
print(indices(['a', 'a/b', 'a/b/a', 'a/c', 'a/c/a', 'b']))
print(indices(['a', 'c', 'b', 'a/b']))
print(indices(['a/b/c/d', 'a/b/d', 'a/b/c']))
print(indices(['abc/d', 'bcc/d']))
print(indices(['apple/cat','apple/dog', 'banana/dog']))
outputs:
['1', '1.1', '1.1.1', '1.2', '1.2.1', '2']
['1', '3', '2', '1.1']
['1.1.1.1', '1.1.2', '1.1.1']
['1.1', '2.1']
['1.1', '1.2', '2.1']
Demo: https://replit.com/#blhsing/StainedMassivePi
Since the parts of the string separated by / can presumably have different lengths, you can't just sort the strings directly. However, by splitting the strings over the /, you can get tuples, which you can sort directly in the way you want:
strings = ['a','a/b/a','a/b','a/b/d', 'b/a', 'b']
keys = sorted(map(lambda s: s.split('/'), strings))
print(keys)
Output:
[['a'], ['a', 'b'], ['a', 'b', 'a'], ['a', 'b', 'd'], ['b'], ['b', 'a']]

How to check if a dictionary has exact same keys in a list

I want to check if a dictionary contains the exact same keys as in a list of keys (no more, no less). I'm currently using all() and a length check to do this. Is there a better way? Thanks!
d = {'1': 'one', '3': 'three', '2': 'two'}
key_list = ['1', '2', '3']
all(col in d for col in key_list) and len(d) == 3
True
What about
set(d) == set(key_list)
set(d) is equals to set(d.keys()) as #gmds pointed out
A list is great for maintaining order, but a set is far better for checking membership:
d = {'1': 'one', '3': 'three', '2': 'two'}
key_list = set(['1', '2', '3'])
all(col in d for col in key_list) and len(d) == 3
set has a lookup time of O(1), compared to list which is O(N)
You can do something like this:
>>> d = {'1': 'one', '3': 'three', '2': 'two'}
>>> key_list = ['1', '2', '3']
>>> sorted(d) == sorted(key_list)
True
>>>

Return a dictionary with keys for a given dictionary's values and vice versa

How would I, in a function, create a dictionary who's keys are a given dictionary's values, and vice versa, where the given dictionary has multiple values per key?
For example, given a dictionary:
d = {"1":["a","b"],"2":["b","a","c"],"3":["c","d"]}
I'd need to return a dictionary like:
d2 = {"a":["1","2"],"b":["1","2"],"c":["2","3"],"d":["3"]}
My only idea was to make a list of all values, then manually check each key for each value, but I couldn't figure out how to do that without knowing the number of keys and values. Any help would be vastly appreciated.
Try:
{letter: [key for key in d if letter in d[key]] for letter in set(letter for key in d for letter in d[key])}
Explanation: set(letter for key in d for letter in d[key]) is a set of all letters that appears in the original dict. Then we make a new dict, in which every entry is letter: [key for key in d if letter in d[key]], which means one of the letters, mapping to a list of numbers that mapped to it in the original dict.
Easy using collections.defaultdict() which defaults as list
loop on the sorted key/value couple
inner loop on the value items
for each value item, create/update list with original dict key
code:
import collections
d3 = collections.defaultdict(list)
for k,v in sorted(d.items()):
for vi in v:
d3[vi].append(k) # create list or append to existing list
print(dict(d3))
result:
{'b': ['1', '2'], 'd': ['3'], 'c': ['2', '3'], 'a': ['1', '2']}
data = [(key, value) for key, values in d.items() for value in values]
d2 = defaultdict(list)
for value, key in data:
d2[key].append(value)
print(key, value)
Here is a solution I found 5 years ago
from operator import itemgetter
from itertools import groupby
d = {'1': ['a', 'c'],'2': ['a', 'b', 'c'], '3': ['b']}
d2 = {x: [t[1] for t in group] for (x, group) in groupby(sorted(((j, k) for k, v in d.items() for j in v), key=itemgetter(0)), key=itemgetter(0))}
# {'a': ['1', '2'], 'c': ['1', '2'], 'b': ['2', '3']}

How to match keys in a list and find there intersecting values?

i have a dictionary with lists and my goal is to match a query list against the dictionary and for matched terms display there intersecting values. For example
dict= [(this, ['1']), (word, ['1', '2']), (search, ['2'])]
searchedQuery = [this, word]
output = 1
Can someone show me the simplest approach in implementing this techniques, i was thinking of using this approach
for key in dict.keys():
...get values
...intersect values
Like this:
>>> d
[('this', ['1']), ('word', ['1', '2']), ('search', ['2'])]
>>> set.intersection(*[set(v) for k,v in d if k in searchedQuery])
set(['1'])
Explanation:
for k,v in d if k in searchedQuery enumerates the pairs in d that have the keys you want
[set(v) ...] makes sets of the values
* in front of the list comprehension unpacks the list so we can pass to set.intersection
set.intersection gives you the intersection.
Asides:
As mentioned in another answer, a list of pairs is not really a dict.
It's considered not a good idea to use dict for your own variable name (but we see what you mean).
How about this:
>>> dic = dict([('this', ['1']), ('word', ['1', '2']), ('search', ['2'])])
>>> searchedQuery = ['this', 'word']
>>> [y for x,y in dic.items() if x in searchedQuery]
[['1'], ['1', '2']]
>>>
You can do exactly the same. But there are few things you need to understand before getting into it.
A dictionary in Python, looks something like this
d = {'this': ['1'], 'search': ['2'], 'word': ['1', '2']}
So, in order to get the data you presented in the form of a dictionary you need to do something like this
d = [('this', ['1']), ('word', ['1', '2']), ('search', ['2'])]
print dict(item for item in d)
And then you can get the values from the dictionaries, corresponding to the searchedQuery and finally do the set intersection like this
print set.intersection(*[set(d.get(item, {})) for item in searchedQuery])
# set(['1'])

Create a list of unique keys in Python

I have a list of
[{"1":"value"},{"1":"second_value"},{"2":"third_value"},{"2":"fourth_value"},{"3":"fifth_value"}]
want to convert it into
[{"1":"value","2":"third_value","3":"fifth_value"},{"1":"second_value","2":"fourth_value"}]
There is probably a cleaner way of doing this, input is appreciated:
d = [{"1":"value"},{"1":"second_value"},{"2":"third_value"},{"2":"fourth_value"},{"3":"fifth_value"}]
results = [{}]
for item in stuff:
j,k = item.items()[0] // Do the initial dicts always contain one key-value pair?
for result in results:
if j not in result:
result[j] = k
break
if result == results[-1]:
results.append(item)
break
Result:
[{'1': 'value', '3': 'fifth_value', '2': 'third_value'}, {'1': 'second_value', '2': 'fourth_value'}]
You can use collections.defaultdict:
>>> import collections
>>> result = collections.defaultdict(list)
>>> for item in d:
... result[item.values()[0]].append(item.keys()[0])
...
>>> [{key: value for key in keys} for value, keys in result.items()]
[{'1': 'second_value', '2': 'second_value'}, {'1': 'value', '3': 'value', '2': 'value'}]
Note that second_value comes before value in this as the ordering is rather arbitrary (unless you were to explicitly specify that value should be ordered before second_value the above would give you the ordering that the dictionary returns).
You can use collections.defaultdict here. Iterate over the list, use the values as keys and collect all the keys related to a value in a list.
>>> from collections import defaultdict
>>> d = defaultdict(list)
for dic in lis:
for k, v in dic.items():
d[v].append(k)
...
Now d becomes:
>>> d
defaultdict(<type 'list'>,
{'second_value': ['1', '2'],
'value': ['1', '2', '3']})
Now iterate over d to get the desired result:
>>> [{v1:k for v1 in v} for k, v in d.items()]
[{'1': 'second_value', '2': 'second_value'}, {'1': 'value', '3': 'value', '2': 'value'}]

Categories

Resources