Let us imagine the following dictionary
dictionary = {
"key1": {
"value": [1, 3, 5],
},
"key2": {
"value": [1, 2, -1],
},
}
Is it possible to set all the "values" to [] without iterating over the dictionary keys? I want something like dictionary[]["value"]=[] such that all "value" attributes are set to []. But that doesn't work.
Because you need to avoid iteration, here is a little hacky way of solving the case.
Convert dictionary to string, replace and then back to dictionary:
import re, ast
dictionary = {
"key1": {
"value": [1, 3, 5],
},
"key2": {
"value": [1, 2, -1],
},
}
print(ast.literal_eval(re.sub(r'\[.*?\]', '[]', str(dictionary))))
# {'key1': {'value': []}, 'key2': {'value': []}}
I'm going to take a different tack here. Your question is a little misinformed. The implication is that it's "better" to avoid iterating dictionary keys. As mentioned, you can iterate over dictionary values. But, since internally Python stores dictionaries via two arrays, iteration is unavoidable.
Returning to your core question:
I want something like dictionary[]["value"]=[] such that all "value"
attributes are set to [].
Just use collections.defaultdict:
from collections import defaultdict
d = {k: defaultdict(list) for k in dictionary}
print(d['key1']['value']) # []
print(d['key2']['value']) # []
For the dictionary structure you have defined, this will certainly be more efficient than string conversion via repr + regex substitution.
If you insist on explicitly setting keys, you can avoid defaultdict at the cost of an inner dictionary comprehension:
d = {k: {i: [] for i in v} for k, v in dictionary.items()}
{'key1': {'value': []}, 'key2': {'value': []}}
Related
I'm trying to get a list of all keys in the nested level of my dictionary.
My dictionary resembles:
my_dict= {
'DICT':{
'level_1a':{
'level_2a':{}
},
'level_1b': {
'level_2b':{},
'level_2c':{}
}
}
My desired output should resemble:
['level_2a', 'level_2b', 'level_2c']
What I've tried:
[list(v) for k, v in json['DICT'].items()]
My current output:
[['level_2a'], ['level_2b', 'level_2c']]
I want my result to be fully flattened to a single-level list. I've tried flattening libraries but the result tends to appear as: ['level_2a', 'level_2blevel_2c'] which is incorrect. Not looking to make the code more complex by creating another method just to flatten this list.
Would appreciate some help, thank you!
Try:
my_dict = {
"DICT": {
"level_1a": {"level_2a": {}},
"level_1b": {"level_2b": {}, "level_2c": {}},
}
}
lst = [vv for v in my_dict["DICT"].values() for vv in v]
print(lst)
Prints:
['level_2a', 'level_2b', 'level_2c']
I have a list of dictionary objects
data = [
{
'id': 1,
'parent_id': 101 ,
'name': 'A'
},
{
'id': 2,
'parent_id': 101,
'name': 'B'
},
{
'id': 3,
'parent_id': 102,
'name': 'C'
},
{
'id': 4,
'parent_id': 102,
'name': 'D'
}
]
I want to convert this to a nested dictionary.
Structure:
{
parent_id_value: {
name_value: id_value
}
}
Result of the sample list of dictionary object should be like this
{
101: {
'A': 1,
'B': 2
},
102: {
'C': 3,
'D': 4
}
}
I know we can run a for loop and use setdefault to set/get paren_id value and then add the name and id as key, value
new_dic = {}
for i in data:
new_dic.setdefault(i['parent_id'], {})[i['name']] = i['id']
print(new_dic)
But I am looking for a more pythonic way, meaning is it possible through dictionary comprehension?
So you want to play the comprehension game and you want to see a pro player at it.
Forget about itertools, forget about multiple statements. Here is the ultimate comprehension that will make any programmer working on your code throw the keyboard away and leave the room cursing you.
data = {parent_id: {d["name"]: d["id"] for d in [i for i in data if i["parent_id"] == parent_id]} for parent_id in set((j["parent_id"] for j in data))}
But for real though, don't do this in your code if it's going to be shared with someone.
First, sort the list using operator.itemgetter to supply the key:
data.sort(key=itemgetter('parent_id'))
Then use itertools.groupby to group the intended sections in a nested dict-comprehension:
data = {
key: {item['name']: item['id'] for item in group}
for key, group in groupby(data, itemgetter('parent_id'))
}
I don't recommend writing a one-liner, but you can do it with sorted:
data = {
key: {item['name']: item['id'] for item in group}
for key, group in groupby(sorted(data, key=itemgetter('parent_id')), itemgetter('parent_id'))
}
So, I need to plot something with matplotlib, and all was going perfect up until this error. Basically, what I need is to turn the following:
[
{
"foo":1,
"bar":2
},
{
"foo":8,
"baz":3
}
]
into something like this:
[
{
"foo":1,
"bar":2,
"baz":0
},
{
"foo":8,
"baz":3,
"bar":0
}
]
Is there any way this can be done?
One way is to calculate the union of your keys and then iterate via a for loop.
keys = set().union(*lst)
for d in lst:
for k in keys - d.keys():
d[k] = 0
print(lst)
[{'foo': 1, 'bar': 2, 'baz': 0},
{'foo': 8, 'baz': 3, 'bar': 0}]
Since we're posting answers... There's an easier way. Simply turn your dicts into defaultdicts wherever it is that you're defining them. - defaultdict(int, your_dictionary)
You can then access this dict for your plot regardless of non-existent keys since if they don't exist the defaultdict will just return 0 here as you want. No point reinventing the wheel here.
If keys is the list of keys and L is the list of dictionaries then:
for d in L:
for k in keys:
d.setdefault(k, 0)
sets each missing key to 0.
I have the following simple data structures:
teams = [ { 'league_id': 1, 'name': 'Kings' }, { 'league_id': 1, 'name': 'Sharkls' }, { 'league_id': 2, 'name': 'Reign' }, { 'league_id': 2, 'name': 'Heat' } ]
leagues = [ { 'league_id': 1, 'name': 'League 1' }, { 'league_id': 2, 'name': 'League 2' } ]
And I have the following dict comprehension:
league_teams = { x['league_id']: [ t['name']
for t in teams if t['league_id'] == x ['league_id'] ]
for x in leagues }
Which yields:
{1: ['Kings', 'Sharkls'], 2: ['Reign', 'Heat']}
Is there a simpler way using itertools or something to get that dict? This feels a little cumbersome.
Here's an adaptation of Moinuddin Quadri's O(n+m) solution that catches the "empty league" case, and which incidentally does not require any modules to be imported. The dict output does double-duty as his league_ids set, and since it's pre-initialized, it does not need to be a collections.defaultdict:
output = { league['league_id']:[] for league in leagues }
for team in teams:
if team['league_id'] in output:
output[team['league_id']].append(team['name'])
print(output)
The output is:
{1: ['Kings', 'Sharkls'], 2: ['Reign', 'Heat']}
You do not need itertools here, instead collections.defaultdict is better choice. Complexity of your solution is O(n*m) whereas with defaultdict, it will be O(n+m).
You can achieve what you want like:
from collections import defaultdict
# create set to store `league_id` in `leagues`. Set holds unique
# values and also searching in set is faster than in normal list
leagues_id = set([item['league_id'] for item in leagues])
my_dict = defaultdict(list)
for item in teams:
if item['league_id'] in leagues_id:
my_dict[item['league_id']].append(item['name'])
where at the end my_dict will hold the value:
{1: ['Kings', 'Sharkls'], 2: ['Reign', 'Heat']}
Edit: In case you also want entry in my_dict for the league_id present in leagues, but not in teams, you need to explictly make entries like:
for leagues_id in leagues_ids:
_ = my_dict[leagues_id] # Will create empty list for such ids
Checking t['league_id'] == x['league_id'] looks not necessary.
You can simplify with:
import collections
league_teams = collections.defaultdict(list)
for t in teams:
league_teams[t['league_id']].append(t['name'])
If you really want itertools for that:
import itertools
league_teams = {k: [t['name'] for t in g]
for k, g in itertools.groupby(teams, key=lambda t: t['league_id'])}
But it will only work if the teams list is sorted.
I know this is going to sound like I just need to use json.loads from the title. But I don't think that's the case here. Or at least I should be able to do this without any libraries (plus I want to understand this and not just solve it with a library method).
What I have currently is a dictionary where the keys are words and the values are total counts for those words:
myDict = { "word1": 12, "word2": 18, "word3": 4, "word4": 45 }
and so on...
what I want is for it to become something like the following (so that I can insert it into a scraperwiki datastore):
myNewDict = {"entry": "word1", "count": 12, "entry": "word2", "count": 18, "entry": "word3", "count": 4, "entry": "word4", "count": 45}
I figured I could just loop over myDict and insert each key/value after my new chosen keys "entry" and "count" (like so):
for k, v in myDict.iteritems():
myNewDict = { "entry": k, "count": v }
but with this, myNewDict is only saving the last result (so only giving me myNewDict={"entry":"word4", "total":45}
what am I missing here?
What you need is a list:
entries = []
for k, v in myDict.iteritems():
entries.append({ "entry": k, "count": v })
Or even better with list comprehensions:
entries = [{'entry': k, 'count': v} for k, v in myDict.iteritems()]
In more details, you were overriding myDict at each iteration of the loop, creating a new dict every time. In case you need it, you could can add key/values to a dict like this :
myDict['key'] = ...
.. but used inside a loop, this would override the previous value associated to 'key', hence the use of a list. In the same manner, if you type:
myNewDict = {"entry": "word1", "count": 12, "entry": "word2", "count": 18, "entry": "word3", "count": 4, "entry": "word4", "count": 45}
you actually get {'count': 45, 'entry': 'word4'} !
Note: I don't know what's the expected format of the output data but JSON-wise, a list of dicts should be correct (in JSON keys are unique too I believe).
While it's not clear 100% clear what output you want, if you want just a string in the format you've outlined above, you could modify your loop to be of the form:
myCustomFormat = '{'
for k, v in myDict.iteritems():
myCustomFormat += '"entry": {0}, "count": {1},'.format(k, v)
# Use slice to trim off trailing comma
myCustomFormat = myCustomFormat[:-1] + '}'
That being said, this probably isn't what you want. As others have pointed out, the duplicative nature of the "keys" will make this somewhat difficult to parse.