3

Let's say I have a list like this:

[('Yadda', 5), ('Yadda', 9), ('Blah', 12), ('Blah', 2), ('Blah', 4)]

And I'd like to convert it to a list like this:

[ [('Yadda', 5), ('Yadda', 9)], [('Blah', 12), ('Blah', 2), ('Blah', 4)] ]

Assuming the list is sorted wrt the predicate on which it should be split -

What's the Pythonic way of doing this?

Is there any function that does this or do I have to write it myself?

5 Answers5

8

You can use itertools.groupby.

from itertools import groupby

l = [('Yadda', 5), ('Yadda', 9), ('Blah', 12), ('Blah', 2), ('Blah', 4)]

l.sort(key=lambda item: item[0])

result = [list(group) for _, group in groupby(l, key=lambda item: item[0])]
stamaimer
  • 6,227
  • 5
  • 34
  • 55
  • 1
    Worth mentioning that the list comprehension is only necessary if you want it as a list. `groupby` on its own returns an iterator, so if all you're doing is iterating, that's good enough. – Silvio Mayolo Aug 07 '17 at 04:20
  • 1
    Or `key=operator.itemgetter(0)` – Steven Rumbalski Aug 07 '17 at 04:30
  • @StevenRumbalski Yes, `operator.itemgetter` is okay. I think use lambda function is more easy to understand the key is a one parameter function. – stamaimer Aug 07 '17 at 04:34
  • 2
    groupby group only the adjacent elements! This results in wrong output for input like `[('Yadda', 9), ('Blah', 12), ('Blah', 2), ('Blah', 4), ('Yadda', 5)]` – Keerthana Prabhakaran Aug 07 '17 at 05:06
  • @KeerthanaPrabhakaran Yes, After test the list you gave. We should sort before `groupby`. – stamaimer Aug 07 '17 at 05:09
1

Groupby of itertools groups the adjacent elements.

>>> from itertools import groupby
>>> l = [('Yadda', 5), ('Yadda', 9), ('Blah', 12), ('Blah', 2), ('Blah', 4)]
>>> print [list(group) for _, group in groupby(l, key=lambda item: item[0])]
[[('Yadda', 5), ('Yadda', 9)], [('Blah', 12), ('Blah', 2), ('Blah', 4)]]
>>>
>>> #if the list is not sorted!     
>>> l2 = [('Yadda', 9), ('Blah', 12), ('Blah', 2), ('Blah', 4), ('Yadda', 5)]
>>> print [list(group) for _, group in groupby(l2, key=lambda item: item[0])]
[[('Yadda', 9)], [('Blah', 12), ('Blah', 2), ('Blah', 4)], [('Yadda', 5)]]
  • Its important to sort the list before you proceed!

So after sorting,

>>> l2 = [('Yadda', 9), ('Blah', 12), ('Blah', 2), ('Blah', 4), ('Yadda', 5)]
>>> get_first=key=lambda item: item[0]
>>> print [list(group) for _, group in groupby(sorted(l2,key=get_first), get_first)]
[[('Blah', 12), ('Blah', 2), ('Blah', 4)], [('Yadda', 5), ('Yadda', 9)]]
  • You can also use filter!

As,

>>> l=[('Yadda', 5), ('Yadda', 9), ('Blah', 12), ('Blah', 2), ('Blah', 4)]
>>> s=set(map(lambda item: item[0],l))
>>> print [filter(lambda x:name in x,l) for name in s]
[[('Blah', 12), ('Blah', 2), ('Blah', 4)], [('Yadda', 5), ('Yadda', 9)]]
  • You can also use itemgetter,

That is,

>>> l=[('Yadda', 5), ('Yadda', 9), ('Blah', 12), ('Blah', 2), ('Blah', 4)]
>>> from operator import itemgetter
>>> s=set(map(itemgetter(0),l))
>>> print [filter(lambda x:name in x,l) for name in s]
[[('Blah', 12), ('Blah', 2), ('Blah', 4)], [('Yadda', 5), ('Yadda', 9)]]
Keerthana Prabhakaran
  • 3,766
  • 1
  • 13
  • 23
0

Assuming, you are splitting the list based on the first item of the inner list, I would use a dictionary.

l = [('Yadda', 5), ('Yadda', 9), ('Blah', 12), ('Blah', 2), ('Blah', 4)]
d={}
for x in l:
  if x[0] not in d:
    d[x[0]]=[x]
  else:
    d[x[0]].append(x)
print(d.values())
voidpro
  • 1,652
  • 13
  • 27
0

I have solved in this way.

list = [('Yadda', 9), ('Blah', 12), ('Blah', 2), ('Blah', 4), ('Yadda', 5)]
ls_set = (set([ls[0] for ls in list]))
ls_dict = {}
for ls in ls_set:
    ls_dict[ls] = []
for ls in list:
    ls_dict[ls[0]].append(ls[1])
final_list = []
for key, value in ls_dict.items():
    a = []
    for i in value:
        a.append(tuple([key,i]))
    final_list.append(a)
print(final_list)
R.A.Munna
  • 1,699
  • 1
  • 15
  • 29
0

if you want without any package or itertools then this will help you,

>>> l = [('Yadda', 5), ('Yadda', 9), ('Blah', 12), ('Blah', 2), ('Blah', 4)]
>>> l.sort(key= lambda x:x[1])
>>> values = set(map(lambda x:x[0], l))
>>> [[y for y in l if y[0]==i] for i in values]
[[('Blah', 2), ('Blah', 4), ('Blah', 12)], [('Yadda', 5), ('Yadda', 9)]]
>>> 
Mohideen bin Mohammed
  • 18,813
  • 10
  • 112
  • 118