I have the following dictionary:
dic={'a': {'aa': [], 'ab': [], 'ac': []}, 'b': {'ba': [], 'bb': [], 'bc': []}}
I want to append three values to either all keys in 'a' or 'b'.
Following example works:
dic['a']['aa'].append(value_a)
dic['a']['ab'].append(value_b)
dic['a']['ac'].append(value_c)
Any way where I can do this in one single line.
What I'm searching for is something like the following:
dic['a'][*] = [value_a, value_b, value_c]
Where * is a wildcard indexing all keys in dic['a'].
As the complexity of the dictionary in my actual program grows my current working example becomes unreadable. So my motivation for this is mainly readability.
If a loop is acceptable for your single-line solution, then this may be used.
for key in dic['a']: dic['a'][key].append(value)
If you have a list of values, you can extend the list.
for key in dic['a']: dic['a'][key].extend([value_a, value_b, value_c])
To use a true one-liner (i.e., not writing a forloop in one line) it is possible to use map and exploit the mutability of dicts:
dic={'a': {'aa': [], 'ab': [], 'ac': []}, 'b': {'ba': [], 'bb': [], 'bc': []}}
vals = [1,2,3]
key = 'a'
map(lambda kv: dic[key][kv[0]].append(kv[1]), zip(dic[key], vals))
This will return [None, None, None] but the dict will be updated. However, I would suggest that it would be better to use an explicit for loop, as a two-liner:
for k, v in zip(dic[key], vals):
dic[key][k].append(v)
Note that this will add a separate value to each of the entries in the dict, as this is what I interpret to be what is wanted.
Related
I have a list of dict that I am struggling to change to list of dicts:
x = [
{ # begin first and only dict in list
'11': [{'bb': '224', 'cc': '14'}, {'bb': '254', 'cc': '16'}]
, '22': [{'bb': '824', 'cc': '19'}]
}
]
Which currently has a length of 1. i.e.:
print(len(x)) # 1
I intend to change it to list of dicts. i.e.:
desired = [
{ # begin first dict in list
'11': [{'bb': '224', 'cc': '14'}, {'bb': '254', 'cc': '16'}]
}
, { # begin second dict in list
'22': [{'bb': '824', 'cc': '19'}]
}
]
print(len(desired)) # 2
What I tried:
dd = {}
for elem in x:
for k,v in elem.items():
dd[k] = v
print([dd])
print(len(dd)) # 2
Is there a better way or perhaps more efficient way?
Like others mentioned, there is nothing "wrong" with what you've tried. However, you might be interested in trying a list comprehension:
x_desired = [{k: v} for i in x for k, v in i.items()]
Like your attempted approach, this is effectively 2 for loops, the second nested within the first. In general, list comprehensions are considered fast and Pythonic.
Do you want the result to be 2 as you"ve got two keys (11 & 22) ?
then try this:
print(len(x[0]))
*edit
I make
word=['I','love','hello','world','love','I']
when I convert to set, It change the order to
print(set(word))
output: {'world', 'I', 'hello', 'love'}
How to sort the set again to be
{'I', 'love', 'hello', 'world'}
Sets are unordered. If you want order, convert back to a list.
E.g.
print(sorted(set(word)))
sorted will sort your items and return a list.
However, if you want to retain the order of your elements rather than sort them, you can use a set for deduplication and a list for ordering, something like this:
def unique(items):
seen = set()
result = []
for item in items:
if item not in seen:
seen.add(item)
result.append(item)
return result
and use it as:
>>> word = ['I','love','hello','world','love','I']
>>> print(unique(word))
['I', 'love', 'hello', 'world']
If you just want an ordered collection of unique values, you can create a dict from the list, either with a dict comprehension or dict.fromkeys. In Python 3, dictionaries will retain insertion order; for older versions, use collections.OrderedDict. The dict will have values besides the keys, but you can just ignore those.
>>> word = ['a','b','c','c','b','e']
>>> {k: None for k in word}
{'a': None, 'b': None, 'c': None, 'e': None}
>>> dict.fromkeys(word)
{'a': None, 'b': None, 'c': None, 'e': None}
Other than sorted, this also works if the original order is different than the sorted order.
>>> word = ['f','a','b','c','c','b','e']
>>> dict.fromkeys(word)
{'f': None, 'a': None, 'b': None, 'c': None, 'e': None}
You can then either convert the result to list or keep it a dict and add more values, but if you make it a set, the order will be lost again. Like a set, the dict also allows fast O(1) lookup, but no set operations like intersection or union.
Here is the code:
from collections import defaultdict
result = defaultdict.fromkeys(['a','b','c'], list)
result['a'].append(1)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-44-6c01c8d56a42> in <module>()
----> 1 result['a'].append('1')
TypeError: descriptor 'append' requires a 'list' object but received a 'str'
I don't understand the error message, what went wrong and how to fix it?
The .fromkeys method is primarily used to set a dict to a single same default value:
>>> {}.fromkeys(['a','b','c'])
{'a': None, 'c': None, 'b': None}
It does not call a function for each key (which is what list or [] is).
Default dict needs a 'factory function' to be a default dict:
>>> from collections import defaultdict
>>> result=defaultdict(list)
>>> result
defaultdict(<type 'list'>, {})
The factory function (in this case, list) is called any time a missing key is added to a defaultdict to form the default value.
So to set three lists with keys 'a','b','c' you would do:
>>> for e in ('a','b','c'):
... result[e] # 'e' is not in the dict, so it is added
# and a new list is the value
>>> result
defaultdict(<type 'list'>, {'a': [], 'c': [], 'b': []})
Or, you can use the .update method as Raymond Hettinger points out:
>>> result=defaultdict(list)
>>> result.update((k,[]) for k in 'abc')
>>> result
defaultdict(<type 'list'>, {'a': [], 'c': [], 'b': []})
Or, as ivan_pozdeev points out in comments, you can do:
>>> di=defaultdict(list,{k:[] for k in 'abc'})
>>> di
defaultdict(<type 'list'>, {'a': [], 'c': [], 'b': []})
Or you can use a regular Python dict with a dict comprehension to get the same thing -- no defaultdict required -- if you only need or want those three keys with unique lists as their values:
>>> di={k:[] for k in 'abc'}
>>> di
{'a': [], 'c': [], 'b': []}
And those are separate lists for each key:
>>> di['a'].append(1)
>>> di
{'a': [1], 'c': [], 'b': []}
A common mistake (which I have made ;-0) is to use something like .fromkeys and get the same list referred to by multiple keys:
>>> di2={}.fromkeys(['a','b','c'], [])
>>> di2
{'a': [], 'c': [], 'b': []} # looks ok
>>> di2['a'].append('WRONG!')
>>> di2
{'a': ['WRONG!'], 'c': ['WRONG!'], 'b': ['WRONG!']}
Happens because the same single list is referred to by all the keys.
Unfortunately, fromkeys() is for setting the same value over and over again. It isn't helpful when you need distinct lists.
So, I would tackle the problem like this:
>>> keys = ['barry', 'tim', 'fredrik', 'alex']
>>> d = defaultdict(list)
>>> d.update((k, []) for k in keys)
>>> d
defaultdict(<class 'list'>, {'barry': [], 'tim': [], 'fredrik': [], 'alex': []})
I have a dictionary of lists, and it should be initialized with default keys. I guess, the code below is not good (I mean, it works, but I don't feel that it is written in the pythonic way):
d = {'a' : [], 'b' : [], 'c' : []}
So I want to use something more pythonic like defaultict:
d = defaultdict(list)
However, every tutorial that I've seen dynamically sets the new keys. But in my case all the keys should be defined from the start. I'm parsing other data structures, and I add values to my dictionary only if specific key in the structure also contains in my dictionary.
How can I set the default keys?
From the comments, I'm assuming you want a dictionary that fits the following conditions:
Is initialized with set of keys with an empty list value for each
Has defaultdict behavior that can initialize an empty list for non-existing keys
#Aaron_lab has the right method, but there's a slightly cleaner way:
d = defaultdict(list,{ k:[] for k in ('a','b','c') })
That's already reasonable but you can shorten that up a bit with a dict comprehension that uses a standard list of keys.
>>> standard_keys = ['a', 'b', 'c']
>>> d1 = {key:[] for key in standard_keys}
>>> d2 = {key:[] for key in standard_keys}
>>> ...
If you're going to pre-initialize to empty lists, there is no need for a defaultdict. Simple dict-comprehension gets the job done clearly and cleanly:
>>> {k : [] for k in ['a', 'b', 'c']}
{'a': [], 'b': [], 'c': []}
If you have a close set of keys (['a', 'b', 'c'] in your example)
you know you'll use, you can definitely use the answers above.
BUT...
dd = defaultdict(list) gives you much more then: d = {'a':[], 'b':[], 'c':[]}.
You can append to "not existing" keys in defaultdict:
>>dd['d'].append(5)
>>dd
>>defaultdict(list, {'d': 5})
where if you do:
>>d['d'].append(5) # you'll face KeyError
>>KeyError: 'd'
Recommend to do something like:
>>d = {'a' : [], 'b' : [], 'c' : []}
>>default_d = defaultdict(list, **d)
now you have a dict holding your 3 keys: ['a', 'b', 'c'] and empty lists as values, and you can also append to other keys without explicitly writing: d['new_key'] = [] before appending
You can have a function defined which will return you a dict with preset keys.
def get_preset_dict(keys=['a','b','c'],values=None):
d = {}
if not values:
values = [[]]*len(keys)
if len(keys)!=len(values):
raise Exception('unequal lenghts')
for index,key in enumerate(keys):
d[key] = values[index]
return d
In [8]: get_preset_dict()
Out[8]: {'a': [], 'b': [], 'c': []}
In [18]: get_preset_dict(keys=['a','e','i','o','u'])
Out[18]: {'a': [], 'e': [], 'i': [], 'o': [], 'u': []}
In [19]:
get_preset_dict(keys=['a','e','i','o','u'],values=[[1],[2,2,2],[3],[4,2],[5]])
Out[19]: {'a': [1], 'e': [2, 2, 2], 'i': [3], 'o': [4, 2], 'u': [5]}
from collections import defaultdict
list(map((data := defaultdict(list)).__getitem__, 'abcde'))
data
Out[3]: defaultdict(list, {'a': [], 'b': [], 'c': [], 'd': [], 'e':
[]})
I have a weird data structure that is returned to me by an external service I have no control over.
The data is essentially a list of dictionaries, but chopped up in a strange way: it is returned as a list of dictionaries, where each of these dictionaries has a single key. Taking multiple elements from this list yields all the keys in the dictionary.
In code:
[ {'id': 1}, {'a': a}, {'b': b}, {'c': c},
{'id': 2}, {'a': a}, {'b': b}, {'c': c},
{'id': 3}, {'a': a}, {'b': b}, {'c': c},
...
]
Every dictionary that I want to reconstruct starts with the id dictionary. After I find the id key, I need to take all the values from the list until I find another id.
My current solution is:
def split_groups(data, key='id'):
groups = []
for e in data:
if key in e: # begin new group
groups.append(list())
groups[-1].append(e)
return groups
which works, but it's ugly. I know about itertools.groupby: however, I can't really understand how to use it.
The result of this line:
[(k, list(g)) for k, g in groupby(data, lambda d: d.get('id') is not None)]
is:
[(True, [{'id': 1}]),
(False, [{'a': 1}, {'b': 2}, {'c': 3}]),
(True, [{'id': 2}]),
(False, [{'a': 1}, {'b': 2}, {'c': 3}]),
(True, [{'id': 3}]),
(False, [{'a': 1}, {'b': 2}, {'c': 3}])]
As you can see the id dictionary ends up in a different group than the following values.
What am I doing wrong?
After Sumukh Barve's answer, I guess that groupby just isn't the right tool for my job. My current code will do for production; just for fun, I rewrote it like this:
def split_groups(data, key='id'):
if not data:
return []
predicate = lambda d: key not in d
head, tail = data[0], data[1:]
group = [[head] + list(takewhile(predicate, tail))]
rest = list(dropwhile(predicate, tail))
group.extend(split_groups(rest, key))
return group
which is a much less efficient, much less readable, much more OCD-appealing form.
Thanks everyone for your help!
Just in case that somebody one day stumbles into my same problem, I am attaching the full solution with some example data.
From the docs:
It (itertools.groupby) generates a break or new group every time the value of the key function changes . . .
In that sense, itertools.groupby is similar to str.split; the difference being that the split sequence is also included in the output.
"1,2,3".split(",") ==> ["1", "2", "3"]
"1,2,3".splitLikeGroupBy(",") ==> ["1", ",", "2", ",", "3"]
So, you aren't doing anything wrong.
Also, I'd say that your solution is good.
But, if you insist on using itertools.groupby, try this:
a = [(k, list(g)) for k, g in groupby(data, lambda d: d.get('id') is not None)];
[a[i][1] + a[i+1][1] for i in range(len(a)) if i % 2 == 0]
The first line comes straight from your code. The second is some simple processing.
Suggestion:
Instead of working with a list of list of single-element dictionaries, you may want to work with a list of multi-element dictionaries.
That is, instead of working with this:
[
[{"id": "id1"}, {"a": "a1"}],
[{"id": "id2"}, {"a": "a2"}], ...
]
You might want to work with this:
[
{"id": "id1", "a": "a1"},
{"id": "id2", "a": "a2"}, ...
]
Hope this helps.
IMHO, this is not a trivial task.
A solution in two lines :
ind=[i for i,d in enumerate(l) if 'id' in d]
slices=[l[a:b] for (a,b) in zip(ind,ind[1:]+[len(l)])]