8

I have a list of list and I want to construct a set out of the elements present in all the sublists..

example : a = [[1,2],[2,3]] should give set([1,2,3])

I tried reduce(lambda x,y:x.update(y),a,set([])) but it raises AttributeError: 'NoneType' object has no attribute 'update'

Can someone tell me how to do this using reduce function ?

Sameera Thilakasiri
  • 9,452
  • 10
  • 51
  • 86
Graddy
  • 2,828
  • 5
  • 19
  • 25
  • Answerede here http://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python with TIME BENCHMARKS – andilabs Jul 22 '15 at 17:10

6 Answers6

16

As requested:

>>> from functools import reduce
>>> a = [[1,2],[2,3]]
>>> reduce(lambda s, elems: s.union(elems), a, set())
set([1, 2, 3])

Another way just for fun:

>>> from itertools import chain
>>> set(chain.from_iterable(a))
set([1, 2, 3])

And one more just to be cool:

>>> set.union(set(), *a)
set([1, 2, 3])
Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
  • @julio.alegria My first version used ``|`` but it looked like a weird separator in the context of ``reduce(lambda s, elems: s | elems, a, set())``. To my eyes, it looks like something that divides ``elem, a, set()`` from the lambda expression. – Raymond Hettinger Nov 30 '11 at 06:13
  • Fix your last example, there should be `set` instead of `s`. – Michał Bentkowski Nov 30 '11 at 09:03
  • have a look on TIME benchmarks here http://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python – andilabs Jul 22 '15 at 17:09
  • note that you'll have to import "functools" in order to use the reduce function via functools.reduce – emmaakachukwu Jun 29 '23 at 10:41
8

The problem is that update() on a set returns None, not the set. This is documented and expected behavior. Assuming you want to use update() for some reason, you could write your lambda as follows:

 lambda x, y: x.update(y) or x

The or clause returns x if the first clause is "falsy" (which None is).

Really, though, I think you want to use union() instead of update(). It does mostly the same thing and returns the result.

lambda x, y: x.union(y) 

BTW, you can just write set() to get an empty set, you don't need set([]). So the rewritten reduce() would be:

reduce(lambda x, y: x.union(y), a, set())

Others have posted additional options, each of which has value for making you think about how they work.

kindall
  • 178,883
  • 35
  • 278
  • 309
2

Kindall already answered how to accomplish this task with reduce() but I think it's less clunky to do it with itertools.chain.from_iterable():

import itertools

a = [[1, 2], [2, 3]]

print set(itertools.chain.from_iterable(a))
Michael Hoffman
  • 32,526
  • 7
  • 64
  • 86
  • +1 and this is extremly fast! Have a look on benchmarks here http://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python – andilabs Jul 22 '15 at 17:08
0

And another one with pure list comprehension:

>>> a = [[1,2],[2,3]]
>>> set([item for sublist in a for item in sublist])
{1, 2, 3}

which is what Making a flat list out of list of lists in Python is about.

Risadinha
  • 16,058
  • 2
  • 88
  • 91
0

Use

c =set();
reduce(lambda x,y:c.update(y),a,c)

Its a set that you are using. Since set([]).update doesn't return a value, and reduce function uses the return from previous operation. It raises a Nonetype has no attribute update.

meson10
  • 1,934
  • 14
  • 21
0

The problem with set.update() is that returns None and itertools .. there has to be a better solution. And there is! I think this is way cleaner:

>>> a = [[1,2],[2,3]]
>>> reduce(lambda x, y: set(x) | set(y), a)
set([1, 2, 3])

Note: | makes a union between sets.

juliomalegria
  • 24,229
  • 14
  • 73
  • 89