Home > Mobile >  Any python trick for flattening a dictionary and sorting it all at once?
Any python trick for flattening a dictionary and sorting it all at once?

Time:12-29

I have this dictionary:

s = [{"label":["College Name"],"points":[{"start":939,"end":956,"text":"Kendriya Vidyalaya"}]},{"label":["College Name"],"points":[{"start":883,"end":904,"text":"Woodbine modern school"}]}]

I can sort it on start like this:

for annotation in sorted(s, key = lambda tup: tup['points'][0]['start']):
    print(annotation)

At the end of the day I need label, start and end. I know that I can access annotation in the loop and get to all the values I need, but then I'm pulling out the start point twice.

Is there any cool python trick to unpack values from a dictionary and sort on one of them all at once?

CodePudding user response:

You mean like this?

for annotation in sorted(s, key = lambda tup: tup['points'][0]['start']):
    print(annotation["label"],annotation["points"][0]["start"],annotation["points"][0]["end"])

Or, only having to identify start by name once:

for annotation in sorted([(x["label"][0],x["points"][0]["start"],x["points"][0]["end"]) for x in s], key = lambda tup: tup[1]):
    print(annotation)

CodePudding user response:

was seeing if there was a way to unpack it AND sort it in one shot.

The trick is to do the unpacking first.

What's the rule that tells us how to turn one of the dicts into the desired filtered data? Simple:

(item['label'], item['points'][0]['start'], item['points'][0]['end'])

How do we apply a rule like that to an entire list? Naturally, with a list comprehension or generator expression:

(
    (item['label'], item['points'][0]['start'], item['points'][0]['end'])
    for item in s
)

How do we sort those results? Using sorted, of course (since sorted creates a new result, it's no problem to pass a generator expression); our key function is now much simpler, since it only has to work with the filtered tuple. Thus:

sorted(
    (
        (item['label'], item['points'][0]['start'], item['points'][0]['end'])
        for item in s
    ),
    key = lambda filtered: filtered[1]
)

CodePudding user response:

Assuming your dictionary keys are always in the order shown, you could convert the list of dictionaries to a rearranged list of tuples (that begin with the 'start' key/value pair). Then sort the tuples and re-form dictionaries from them.

s = [{"label":["College Name"],
     "points":[{"start":939,"end":956,"text":"Kendriya Vidyalaya"}]},
     {"label":["College Name"],
      "points":[{"start":883,"end":904,"text":"Woodbine modern school"}]}]

r = [*map(dict,sorted( ((*pd.items(),(lb,lv)) for d in s   
                       for (lb,(lv,*_)),(_,(pd,*_)) in [(*d.items(),)])))]

print(r)

[{'start': 883, 'end': 904, 'text': 'Woodbine modern school',
  'label': 'College Name'},
 {'start': 939, 'end': 956, 'text': 'Kendriya Vidyalaya', 
  'label': 'College Name'}]

If you don't want to flatten the 'points' key but only remove the unnecessary lists, you could do it like this:

r = sorted(({k:v for k,(v,*_) in d.items()} for d in s),
            key=lambda d:d["points"]["start"])

print(r)

[{'label': 'College Name', 
  'points': {'start': 883, 'end': 904, 'text': 'Woodbine modern school'}}, 
 {'label': 'College Name', 
  'points': {'start': 939, 'end': 956, 'text': 'Kendriya Vidyalaya'}}]
  • Related