Reputation: 1244
I've got an object that looks something like this:
obj = {
"outer_list": [
{
"inner_list_1": ["string1", "string2"]
},
{
"inner_list_2": ["string3", "string4"]
}
]
}
I want to create a new list that is the combination of both inner lists:
["string1", "string2", "string3", "string4"]
Currently, I've got something like this:
result = []
for inner_object in obj['outer_list']:
for my_str in inner_object['inner_list']:
result.append(my_str)
I'm wondering how to write that nested loop in a single line.
result = [...]
Upvotes: 1
Views: 687
Reputation: 3591
[item for inner_dict in obj["outer_list"] for item in list(inner_dict.values())[0]]
A list comprehension is made up of two things: an expression, and a loop. You can think of a list comprehension being made as being shorthand for list(get_items())
, where get_items
is defined as
def get_items():
loop:
yield(expression)
In this case, loop
is
for inner_dict in obj["outer_list"]:
for item in list(inner_dict.values())[0]:
and expression
is item
.
So, the format for the list comprehension is to first write what's being yield
ed (in this case, item
), then writing the loop that is iterated over to get those yields.
If you want, you can try it out:
def get_items():
for inner_dict in obj["outer_list"]:
for item in list(inner_dict.values())[0]:
yield(item)
list(get_items())
The loop could also have been written as
for inner_dict in obj["outer_list"]:
for inner_list in list(inner_dict.values()):
for item in inner_list:
yield(item)
which would have resulted in the list comprehension
[item for inner_dict in obj["outer_list"] for inner_list in inner_dict.values() for item in inner_list]
Upvotes: 0
Reputation: 663
You can use reduce
to achieve this
from functools import reduce
result = list(reduce(lambda x,y: x['inner_list']+y['inner_list'], obj['outer_list']))
Upvotes: 0
Reputation: 106553
You can use itertools.chain.from_iterables
:
from itertools import chain
list(chain.from_iterable(d['inner_list'] for d in obj['outer_list']))
So that given:
obj = {
"outer_list": [
{
"inner_list": ["string1", "string2"]
},
{
"inner_list": ["string3", "string4"]
}
]
}
This returns:
['string1', 'string2', 'string3', 'string4']
Or if you want a solution for the data you posted in the question, where the key to the inner list in each sub-dict ends with an incremental number starting from 1, you can use enumerate
to generate such incremental numbers:
list(chain.from_iterable(d['inner_list_%d' % n] for n, d in enumerate(obj['outer_list'], 1)))
So that given:
obj = {
"outer_list": [
{
"inner_list_1": ["string1", "string2"]
},
{
"inner_list_2": ["string3", "string4"]
}
]
}
This returns:
['string1', 'string2', 'string3', 'string4']
Upvotes: 1
Reputation: 111
You can use comprehension to do it, but it runs some risks depending on the inner objects:
resultList = [ stringVal for stringVal in x for x in y.values() for y in obj['outer_list']]
>>> resultList
['string3', 'string3', 'string4', 'string4']
Upvotes: 0
Reputation: 6920
Try this :
result = []
for inner_object in obj['outer_list']:
for k in inner_object:
result.extend(inner_object[k])
Here is an one-liner :
result = [i for inner_object in obj['outer_list'] for v in inner_object.values() for i in v]
Output :
['string1', 'string2', 'string3', 'string4']
Upvotes: 0
Reputation: 6179
If I HAD to do it in one line, I'd probably do it like this.
import itertools
result = list(itertools.chain.from_iterable([tuple(el.values())[0] for el in d['outer_list']]))
yields
['string1', 'string2', 'string3', 'string4']
But the solution you already came up with is much better.
Upvotes: 0
Reputation: 195
The nested notation in Python is the most weird i ever seen.
Its construction [x for y in z for x in y]
result = [ my_str for inner_object in obj['outer_list'] for my_str in inner_object ['inner_list']]
Upvotes: 0