Python lists -- deduping and adding sub-list element

Viewed 60

Input:

a = [[['a','b'],3],[['b','a'],9],[['b','z'],4]]

Desired Output (dedupe regardless of order and add integers)

[[['a','b'],12],[['b','z'],4]]

This does not work of course:

a2 = [sorted(list) for list in [i[0] for i in a]]

print(a2)

[['a', 'b'], ['a', 'b'], ['b', 'z']]

which can be deduped of course:

a3= [] 

for i in a2: 
    if i not in a3: 
       a3.append(i)

print(a3)

[['a', 'b'], ['b', 'z']]

But of course I am losing the counts for the sub-list being deduped. Any advice?
Thanks.

4 Answers

I guess this is what you want:

a = [[['a','b'],3],[['b','a'],9],[['b','z'],4]]

# we use a dict to do the job.
# we sort the list of char and change to tuple. So i can be use as key.
dct = {}

for i in a:
    srt = tuple(sorted(i[0]))
    if srt in dct:
        # the tuple of char is already existing as key, just sum int.
        dct[srt]+= i[1]
    else:
        # if not we had a new key
        dct[srt] = i[1]
# Convert the dict to list of list
new_list = [[list(k),v] for k,v in dct.items()]
print(new_list) # [[['a', 'b'], 12], [['b', 'z'], 4]]

you can use:

from collections import defaultdict

a = [[['a','b'],3],[['b','a'],9],[['b','z'],4]]
d = defaultdict(lambda : 0)

for e, n in a:
    d[frozenset(e)] += n

a =  [[list(k), v]  for k, v in d.items()]
a

output:

[[['a', 'b'], 12], [['b', 'z'], 4]]

or you can use:

from functools import reduce
from operator import add
from collections import Counter

f = ({frozenset(k): v} for k, v in a) 
d = reduce(add, map(Counter, f)) # sum unique data
result = [[list(k), v]  for k, v in d.items()]
result

output:

[[['a', 'b'], 12], [['b', 'z'], 4]]

if you like one-line solution:

 [[list(k), v] for k, v in reduce(add, map(Counter, ({frozenset(k): v} for k, v in a))).items()]

output:

[[['a', 'b'], 12], [['b', 'z'], 4]]

You can convert the sub-lists to frozensets first so that you can treat them as keys and use collections.Counter to add the integers as counts for each distinct key, and then convert the frozensets back to lists when iterating through the resulting items:

from collections import Counter
counter = Counter()
for t, c in a:
    counter.update({frozenset(t): c})
print([[list(t), c] for t, c in counter.items()])

This will do the trick:

from itertools import groupby

a = [[['a','b'],3],[['b','a'],9],[['b','z'],4]]

# this will ensure a is ordered by the merging key:
a=sorted(a, key=lambda x: sorted(x[0]))

# this will group by merging key, extract the value with index 1, and sum it:
a=[[k, sum(map(lambda z: z[1], v))] for k,v in groupby(a, key=lambda x: sorted(x[0]))]

Outputs:

[[['a', 'b'], 12], [['b', 'z'], 4]]
Related