Hana
Hana

Reputation: 824

Create list from nested list in dictionary

With below dictionary, I want to make a new single list with all directories:

nominated = {1931: ['Norman Taurog', 'Wesley Ruggles', 'Clarence Brown', 'Lewis Milestone', 'Josef Von Sternberg'],
             1932: ['Frank Borzage', 'King Vidor', 'Josef Von Sternberg'],
             1933: ['Frank Lloyd', 'Frank Capra', 'George Cukor']}

Desired output: 1 single list with all directors

all_directors = ['Norman Taurog', 'Wesley Ruggles', 'Clarence Brown', 'Lewis Milestone', 'Josef Von Sternberg','Frank Borzage', 'King Vidor', 'Josef Von Sternberg','Frank Lloyd', 'Frank Capra', 'George Cukor']

Attempt 1: list comprehension

all_directors = [[director for director in nominated_directors] for year, nominated_directors in nominated.items()]

print(all_directors)

Output attempt 1

[['Norman Taurog', 'Wesley Ruggles', 'Clarence Brown', 'Lewis Milestone', 'Josef Von Sternberg'], ['Frank Borzage', 'King Vidor', 'Josef Von Sternberg'], ['Frank Lloyd', 'Frank Capra', 'George Cukor']]

Attempt 1: using for loop

all_directors = []
for year, directors in nominated.items():
    for director in directors:
        all_directors.append(director)

print(all_directors)

Output attempt 2

['Norman Taurog', 'Wesley Ruggles', 'Clarence Brown', 'Lewis Milestone', 'Josef Von Sternberg', 'Frank Borzage', 'King Vidor', 'Josef Von Sternberg', 'Frank Lloyd', 'Frank Capra', 'George Cukor']

The output is correct with for loop but not list comprehension. Not sure what I missed, can you please help?

Upvotes: 0

Views: 80

Answers (7)

ukBaz
ukBaz

Reputation: 8014

There are tools in Python's itertools library that could help you with this task such as chain.from_iterable

I saw in the comments that the ultimate goal for this data was to count how many nominations each director had gotten. This can be done with Python's collections library and the Counter functionality.

As an example:

from collections import Counter
import itertools
from prettyprint


nominated = {1931: ['Norman Taurog', 'Wesley Ruggles', 'Clarence Brown', 'Lewis Milestone', 'Josef Von Sternberg'],
             1932: ['Frank Borzage', 'King Vidor', 'Josef Von Sternberg'],
             1933: ['Frank Lloyd', 'Frank Capra', 'George Cukor']}

director_count = Counter(itertools.chain.from_iterable(nominated.values()))
pprint(director_count)

Which gave the output:

Counter({'Josef Von Sternberg': 2,
         'Norman Taurog': 1,
         'Wesley Ruggles': 1,
         'Clarence Brown': 1,
         'Lewis Milestone': 1,
         'Frank Borzage': 1,
         'King Vidor': 1,
         'Frank Lloyd': 1,
         'Frank Capra': 1,
         'George Cukor': 1})

Upvotes: 0

gregory
gregory

Reputation: 13023

Use a generic function to flatten the dictionary's values:

def flatten(items):
    """Yield items from any nested iterable"""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            for sub_x in flatten(x):
                yield sub_x
            else:
                yield x

list(flatten(nominated.values()))

The function will handle greater depth nested lists than the 2d one in this scenario.

Upvotes: 0

Richard Plester
Richard Plester

Reputation: 54

The problem you have here is the order you are iterating over items.

Your for loop correctly iterates over the major items (the sub lists of director names), then within that looping loops over the minor items (the actual names).

For some reason you've reversed this in your attempt at a list comprehension!

Sorting this out we have..

nominated = {1931: ['Norman Taurog', 'Wesley Ruggles', 'Clarence Brown', 'Lewis Milestone', 'Josef Von Sternberg'],
             1932: ['Frank Borzage', 'King Vidor', 'Josef Von Sternberg'],
             1933: ['Frank Lloyd', 'Frank Capra', 'George Cukor']}

all_directors  = [name for year,director_sublist in nominated.items() for name in director_sublist]

print(all_directors)


['Norman Taurog', 'Wesley Ruggles', 'Clarence Brown', 'Lewis Milestone', 'Josef Von Sternberg', 'Frank Borzage', 'King Vidor', 'Josef Von Sternberg', 'Frank Lloyd', 'Frank Capra', 'George Cukor']

Incidentally we don't care about the dictionary keys (the years), so can dispense with them and just iterate over the dictionary values (the lists of director names)..

all_directors  = [name for director_sublist in nominated.values() for name in director_sublist]

Finally, why not make this a set rather than a list comprehension? Get rid of the duplicated names just by replacing [...] with {...}

nominated = {1931: ['Norman Taurog', 'Wesley Ruggles', 'Clarence Brown', 'Lewis Milestone', 'Josef Von Sternberg'],
             1932: ['Frank Borzage', 'King Vidor', 'Josef Von Sternberg'],
             1933: ['Frank Lloyd', 'Frank Capra', 'George Cukor']}

all_directors  = {name for director_sublist in nominated.values() for name in director_sublist}

print(all_directors)

{'Frank Lloyd', 'Frank Capra', 'King Vidor', 'Josef Von Sternberg', 'Wesley Ruggles', 'Norman Taurog', 'Lewis Milestone', 'Frank Borzage', 'Clarence Brown', 'George Cukor'}

Upvotes: 1

M2014
M2014

Reputation: 1194

You problem is a nested list comprehension case. Below is the solution.

all_directors = [director for nominated_directors in nominated.values() for director in nominated_directors]

Upvotes: 0

ramasiva karri
ramasiva karri

Reputation: 1

you can use this nested loop in list comperhension to get all directories into single list

all_directors =[director for year, directors in nominated.items() for director in directors] print(all_directors)

Upvotes: 0

Zac Anger
Zac Anger

Reputation: 7787

You can use sum to flatten the result.

all_directors = sum([[director for director in nominated_directors] for year,
                     nominated_directors in nominated.items()],
                    [])

You can also slim it down using map:

all_directors = sum(list(map(list, nominated.values())), [])

Or even further (thanks to Kelly Bundy in the comments) with:

sum(nominated.values(), [])

Upvotes: 0

Steven
Steven

Reputation: 311

all_directors = [
    director for year, nominated_directors in nominated.items() 
    for director in nominated_directors
    ]

You can just remove the list notation from attempt 1 and re-order the for loops in list comprehension.

Upvotes: 1

Related Questions