Python – split one list to smaller lists conditionally

Question:

Consider following list

all_values = [
    {"a": "first_type"},
    {"a": "second_type"}, {"a": "second_type"},
    {"a": "third_type"}
]

I would like to build a dict:

sorted_objs = {
   "first": [{"a": "first_type"}],
   "second": [{"a": "second_type"}, {"a": "second_type"}],
   "third": [{"a": "third_type"}]
}

What I do:

for obj in all_values:
    if obj["a"] == "first_type":
        sorted_objs["first"].append(obj)
    elif obj["a"] == "second_type":
        sorted_objs["second"].append(obj)
    elif obj["a"] == "third_type":
        sorted_objs["third"].append(obj)

I wonder how I can improve that? All_values list might be a little bit long so maybe I should think about performance too.

Asked By: ifeeltiredboss

||

Answers:

You can use collections.defaultdict and append each dict in a list base key on split('_'):

from collections import defaultdict
res = defaultdict(list)

for dct in all_values:
    for k,v in dct.items():
        num = v.split('_')[0]
        res[num].append(dct)
print(res)

Another approach base dict.setdefault and changing the format of keys at the end:

res = {}
for dct in all_values:
    for k,v in dct.items():
        res.setdefault(v, []).append(dct)
res = {k.split('_')[0]:v for k,v in res.items()}
print(res)

Output:

{
    'first': [{'a': 'first_type'}], 
    'second': [{'a': 'second_type'}, {'a': 'second_type'}], 
    'third': [{'a': 'third_type'}]
}
Answered By: I'mahdi

There’s not a lot of room for improvement, here’s my suggestion:

-Add a list defining the desired order

all_values = [
    {"a": "first_type"},
    {"a": "second_type"}, {"a": "second_type"},
    {"a": "third_type"}
]

order = ["first_type","second_type","third_type"]

def orderSort(li):
    res = {}
    for name in order:
        res[name] = [e for e in li if (name==e["a"])]
    return res

print(orderSort(all_values))

Output:

{
   'first_type': [{'a': 'first_type'}],
   'second_type': [{'a': 'second_type'}, {'a': 'second_type'}],
   'third_type': [{'a': 'third_type'}]
}
Answered By: Cyberflixt

Below function can be used to extract keys and then update the list

all_values = [
{"a": "first_type"},
{"a": "second_type"}, {"a": "second_type"},
{"a": "third_type"}]

sorted_obj={}
for item in all_values:
    element=list( item.values())[0].split('_')[0]
    if  element in sorted_obj.keys():
        sorted_obj[element].append(item)
    else:
        sorted_obj[element]=[item]

output

{'first': [{'a': 'first_type'}],
 'second': [{'a': 'second_type'}, {'a': 'second_type'}],
 'third': [{'a': 'third_type'}]}
Answered By: durgaprasad.sdp

You’d better look up obj["a"] with a dictionary instead of those if/else. Here I build one that maps it to the append method of the list it belongs in (Try it online!):

append = {
    k + "_type": a.append
    for k, a in sorted_objs.items()
}.get

for obj in all_values:
    append(obj["a"], id)(obj)

Using id to ignore keys that don’t exist is a bit of a hack, you could do ignore = id or def ignore(_): pass and then use that to make it clearer.

Some more explanation:

Your code (under "What I do:") means you must already have sorted_objs = {"first": [], "second": [], "third": []}. So I take advantage of that to prepare a helper dict where key "first_type" maps to the append method of the first list, etc. Then I go through your objs and use my helper dict’s get method to get the appropriate append method (or, corresponding to your non-existent else branch, a "fake" append function that has no effect). Then I call that to append (or ignore) the obj.

Answered By: Kelly Bundy
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.