This preserves the order of the first occurence of each key, as well as the order of items for each key. It requires the key to be hashable, but does not otherwise assign meaning to it.
from operator import itemgetter
from itertools import groupby
lki = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]
lki.sort(key=itemgetter(1))
glo = [[x for x,y in g]
for k,g in groupby(lki,key=itemgetter(1))]
print glo
.
EDIT
Another solution that needs no import , is more readable, keeps the orders, and is 22 % shorter than the preceding one:
oldlist = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]
newlist, dicpos = [],{}
for val,k in oldlist:
if k in dicpos:
newlist[dicpos[k]].extend(val)
else:
newlist.append([val])
dicpos[k] = len(dicpos)
print newlist
Howard's answer is concise and elegant, but it's also O(n^2) in the worst case. For large lists with large numbers of grouping key values, you'll want to sort the list first and then use itertools.groupby:
>>> from itertools import groupby
>>> from operator import itemgetter
>>> seq = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]
>>> seq.sort(key = itemgetter(1))
>>> groups = groupby(seq, itemgetter(1))
>>> [[item[0] for item in data] for (key, data) in groups]
[['A', 'C'], ['B'], ['D', 'E']]
Edit:
I changed this after seeing eyequem's answer: itemgetter(1) is nicer than lambda x: x[1].
Basically, if the list is sorted, it is possible to reduce by looking at the last group constructed by the previous steps - you can tell if you need to start a new group, or modify an existing group. The ... or l bit is a trick that enables us to use lambda in Python. (append returns None. It is always better to return something more useful than None, but, alas, such is Python.)
if using convtools library, which provides a lot of data processing primitives and generates ad hoc code under the hood, then:
from convtools import conversion as c
my_list = [["A", 0], ["B", 1], ["C", 0], ["D", 2], ["E", 2]]
# store the converter somewhere because this is where code generation
# takes place
converter = (
c.group_by(c.item(1))
.aggregate(c.ReduceFuncs.Array(c.item(0)))
.gen_converter()
)
assert converter(my_list) == [["A", "C"], ["B"], ["D", "E"]]
from operator import itemgetter
def group_by(nested_iterables: Iterable[Iterable], key_index: int) \
-> List[Tuple[Any, Iterable[Any]]]:
""" Groups elements nested in <nested_iterables> based on their <key_index>_th element.
Behaves similarly to itertools.groupby when the input to the itertools function is sorted.
E.g. If <nested_iterables> = [(1, 2), (2, 3), (5, 2), (9, 3)] and
<key_index> = 1, we will return [(2, [(1, 2), (5, 2)]), (3, [(2, 3), (9,3)])].
Returns:
A list of (group_key, values) tuples where <values> is an iterator of the iterables in
<nested_iterables> that all have their <key_index>_th element equal to <group_key>.
"""
group_keys = set(map(itemgetter(key_index), nested_iterables))
return [(key, list(filter(lambda x: x[key_index] == key, nested_iterables)))
for key in group_keys]