Human Cyborg Relations
Human Cyborg Relations

Reputation: 1244

Single line nested for loop in Python

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

Answers (7)

Acccumulation
Acccumulation

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 yielded (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

noufel13
noufel13

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

blhsing
blhsing

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

huma474
huma474

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

Arkistarvh Kltzuonstev
Arkistarvh Kltzuonstev

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

Tom Wojcik
Tom Wojcik

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

snamef
snamef

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

Related Questions