Re-structuring a list of Python Dicts using setdefault

柔情痞子 提交于 2019-12-24 12:47:58

问题


I am trying to re-structure a list of Python dictionaries by 'grouping' (that's probably not the correct expression, but using it as a proxy based on SQL) the dictionaries based on a (non-unique) value. I have got close, however I'm falling at the final hurdle, in that I couldn't work out how to re-assign each value to a name (i.e. I end up with what looks like a tuple rather than a dict).

Additionally I have a superfluous list (i.e. my output ends up as [[{...}]] rather than [{...}].

I have used the example here:

How do I group this list of dicts by the same month?

Which gets me quite close to what I want, however I'm stuck at the final stage!

market = [
    {'selection_id': 1099, 'value': '11', 'value_name': 'a'},
    {'selection_id': 1099, 'value': '78', 'value_name': 'p'},
    {'selection_id': 1097, 'value': '39', 'value_name': 'b'},
    {'selection_id': 1097, 'value': '52', 'value_name': 'f'},
    {'selection_id': 1098, 'value': '98', 'value_name': 'd'},
    {'selection_id': 1099, 'value': '13', 'value_name': 'y'},
    {'selection_id': 1098, 'value': '4', 'value_name': 'r'},
]

new_structure = {}
new_structure2 = []

for z in market:
        new_structure.setdefault(z['selection_id'], []).append((z['value'], z['value_name']))
        t = [{'selection_id': m, 'value_dict': n} for m, n in new_structure.items()]
new_structure2.append(t)

print(new_structure2)

This outputs as:

[[{'selection_id': 1099, 'value_dict': [('11', 'a'), ('78', 'p'), ('13',  
 'y')]}, {'selection_id': 1097, 'value_dict': [('39', 'b'), ('52', 'f')]},  
 {'selection_id': 1098, 'value_dict': [('98', 'd'), ('4', 'r')]}]]

Which is very close, however what I'm aiming for is:

[{'selection_id': 1099,
  'value_dict': [{'value': '11', 'value_name': 'a'},
                 {'value': '78', 'value_name': 'p'},
                 {'value': '13', 'value_name': 'y'}]},
 {'selection_id': 1097,
  'value_dict': [{'value': '39', 'value_name': 'b'},
                 {'value': '52', 'value_name': 'f'}]},
 {'selection_id': 1098,
  'value_dict': [{'value': '98', 'value_name': 'd'},
                 {'value': '4', 'value_name': 'r'}]}]

I appreciate its probably a really simple fix, but its escaping me at the moment, so any guidance would be greatly appreciated!


回答1:


Here are some hints:

The first thing is to sort by selection_id:

by_selection_id = operator.itemgetter('selection_id')
market.sort(key=by_selection_id)

Then you can group by selection_id:

for selection_id, group in itertools.groupby(market, key=by_selection_id):
    print(selection_id, list(group))

You get:

(1097, [{'value_name': 'b', 'value': '39', 'selection_id': 1097},
        {'value_name': 'f', 'value': '52', 'selection_id': 1097}])
(1098, [{'value_name': 'd', 'value': '98', 'selection_id': 1098},
        {'value_name': 'r', 'value': '4', 'selection_id': 1098}])
(1099, [{'value_name': 'a', 'value': '11', 'selection_id': 1099},
        {'value_name': 'p', 'value': '78', 'selection_id': 1099},
        {'value_name': 'y', 'value': '13', 'selection_id': 1099}])

Then it is easy to build the final list.

Here is a solution using comprehension list/dict:

new_structure = [{'selection_id': selection_id,
                  'value_dict': [{'value': item['value'],
                                  'value_name': item['value_name']} for item in group]}
                 for selection_id, group in itertools.groupby(market, key=by_selection_id)]

Or using a classic list with append:

new_structure = []
for selection_id, group in itertools.groupby(market, key=by_selection_id):
    value_dict = [{'value': item['value'], 'value_name': item['value_name']} for item in group]
    new_structure.append({'selection_id': selection_id,
                          'value_dict': value_dict})



回答2:


I end up with what looks like a tuple rather than a dict)

This is because you are appending a tuple, not a dict:

.append((z['value'], z['value_name']))



回答3:


So, just to update, based on the answer from @Code-Apprentice, I have re-written my code as follows:

market = [
    {'selection_id': 1099, 'value': '11', 'value_name': 'a'},
    {'selection_id': 1099, 'value': '78', 'value_name': 'p'},
    {'selection_id': 1097, 'value': '39', 'value_name': 'b'},
    {'selection_id': 1097, 'value': '52', 'value_name': 'f'},
    {'selection_id': 1098, 'value': '98', 'value_name': 'd'},
    {'selection_id': 1099, 'value': '13', 'value_name': 'y'},
    {'selection_id': 1098, 'value': '4', 'value_name': 'r'},
]

new_structure = {}
new_structure2 = []

for z in market:
    new_structure.setdefault(z['selection_id'], []).append({'value': z['value'],
                                                            'value_name': z['value_name']})
    t = [{'selection_id': m, 'value_dict': n} for m, n in new_structure.items()]
    new_structure2.append(t)

print(new_structure2)

Which then gets me very close to my desired output. The only remaining issue I have, is a superfluous list (i.e. [[{....}]] instead of [{...}]), I believe this is probably because of the way I have written the line t=....

Here is my output, I will post an update, once I've got rid of that extra list in case the answer is of use to anyone else (but please feel free to shout if you spot it before me):

[[{'selection_id': 1099,
   'value_dict': [{'value': '11', 'value_name': 'a'},
                  {'value': '78', 'value_name': 'p'},
                  {'value': '13', 'value_name': 'y'}]},
  {'selection_id': 1097,
   'value_dict': [{'value': '39', 'value_name': 'b'},
                  {'value': '52', 'value_name': 'f'}]},
  {'selection_id': 1098,
   'value_dict': [{'value': '98', 'value_name': 'd'},
                  {'value': '4', 'value_name': 'r'}]}]]

So ridiculously basic! I was over-complicating it, just simply removed the extra list reference:

market = [
    {'selection_id': 1099, 'value': '11', 'value_name': 'a'},
    {'selection_id': 1099, 'value': '78', 'value_name': 'p'},
    {'selection_id': 1097, 'value': '39', 'value_name': 'b'},
    {'selection_id': 1097, 'value': '52', 'value_name': 'f'},
    {'selection_id': 1098, 'value': '98', 'value_name': 'd'},
    {'selection_id': 1099, 'value': '13', 'value_name': 'y'},
    {'selection_id': 1098, 'value': '4', 'value_name': 'r'},
]

new_structure = {}

for z in market:
    new_structure.setdefault(z['selection_id'], []).append({'value': z['value'],
                                                            'value_name': z['value_name']})

new_structure2 = [{'selection_id': m, 'value_dict': n} for m, n in new_structure.items()]


来源:https://stackoverflow.com/questions/56156089/re-structuring-a-list-of-python-dicts-using-setdefault

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!