List comprehension, check if item is unique

20,554

Solution 1

You can use set and set comprehension:

{hobby for name, hobbies in input.items() for hobby in hobbies}

As m.wasowski mentioned, we don't use the name here, so we can use item.values() instead:

{hobby for hobbies in input.values() for hobby in hobbies}

If you really need a list as the result, you can do this (but notice that usually you can work with sets without any problem):

list({hobby for hobbies in input.values() for hobby in hobbies})

Solution 2

As this answer suggests: you can use a uniqueness filter:

def f7(seq):
    seen = set()
    seen_add = seen.add
    return [x for x in seq if not (x in seen or seen_add(x))]

and call with:

>>> f7(hobby for name, hobbies in input.items() for hobby in hobbies)
['running', 'engineering', 'dancing', 'art', 'theatre', 'music']

I would implement the uniqueness filter separately since a design rule says "different things should be handled by different classes/methods/components/whatever". Furthermore you can simply reuse this method if necessary.

Another advantage is - as is written at the linked answer - that the order of the items is preserved. For some applications, this might be necessary.

Solution 3

If you really really want a listcomp and only a list-comp, you can do

>>> s = []
>>> [s.append(j)  for i in d.values() for j in i if j not in s]
[None, None, None, None, None, None]
>>> s
['dancing', 'art', 'theatre', 'running', 'engineering', 'music']

Here, s is a result of a side effect and d is your original dictionary. The unique advantage here is that you can preserve the order unlike most other answers here.

Note: This a bad way as it exploits the list-comp and the result is a side effect. Don't do it as a practice, This answer is just to show you that you can achieve it using a list comp alone

Solution 4

sets and dictionaries are your friends here:

from collections import OrderedDict
from itertools import chain # 'flattens' collection of iterables

data = {
    "Stefan" : ["running", "engineering", "dancing"],
    "Bob" : ["dancing", "art", "theatre"],
    "Julia" : ["running", "music", "art"]
}

# using set is the easiest way, but sets are unordered:
print {hobby for hobby in chain.from_iterable(data.values())}
# output:
# set(['art', 'theatre', 'dancing', 'engineering', 'running', 'music'])


# or use OrderedDict if you care about ordering:
print OrderedDict(
        (hobby, None) for hobby in chain.from_iterable(data.values())
    ).keys()
# output:
# ['dancing', 'art', 'theatre', 'running', 'engineering', 'music']

Solution 5

There's another way of writing this that is a bit more descriptive of what you're actually doing, and doesn't require a nested (double for) comprehension:

output = set.union(*[set(hobbies) for hobbies in input_.values()])

This becomes even nicer when you'd represent the input to be more conceptually sound, i.e. use a set for the hobbies of each person (since there shouldn't be repetitions there either):

input_ = {
    "Stefan" : {"running", "engineering", "dancing"},
    "Bob" : {"dancing", "art", "theatre"}, 
    "Julia" : {"running", "music", "art"}
}

output = set.union(*input_.values())
Share:
20,554
Stefan Bossbaly
Author by

Stefan Bossbaly

Programmer and engineer. Want to learn as much as possible about computers from high level languages like Python to hardware circuitry like flip flops. Blog Github Twitter

Updated on May 02, 2021

Comments

  • Stefan Bossbaly
    Stefan Bossbaly about 3 years

    I am trying to write a list comprehension statement that will only add an item if it's not currently contained in the list. Is there a way to check the current items in the list that is currently being constructed? Here is a brief example:

    Input

    {
        "Stefan" : ["running", "engineering", "dancing"],
        "Bob" : ["dancing", "art", "theatre"],
        "Julia" : ["running", "music", "art"]
    }
    

    Output

    ["running", "engineering", "dancing", "art", "theatre", "music"]
    

    Code without using a list comprehension

    output = []
    for name, hobbies in input.items():
        for hobby in hobbies:
            if hobby not in output:
                output.append(hobby)
    

    My Attempt

    [hobby for name, hobbies in input.items() for hobby in hobbies if hobby not in ???]