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.
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 key
s 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'}]
}
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'}]
}
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'}]}
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 obj
s 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
.
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.
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 key
s 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'}]
}
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'}]
}
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'}]}
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 obj
s 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
.