Home > Net >  Sum list of dicts of lists
Sum list of dicts of lists

Time:01-11

I have a list of dicts, every value in a dict is a four-element list:

my_dict=[
    {
      'prop1': [1, 2, 3, 4],
      'prop2': [1, 1, 0, 0]
    },
    {
      'prop1': [2, 3, 3, 1],
      'prop3': [1, 1, 0, 0]
    }
]

Is it possible to sum it up without writing explicit iteration?

I want to get:

my_dict_sum={
      'prop1': [3, 5, 6, 5],
      'prop2': [1, 1, 0, 0],
      'prop3': [1, 1, 0, 0]
}

UPD: something like this works, but I wonder how to use map or zip or functools to do the same without writing two levels of iteration:

my_dict_sum = {}
for val in my_dict:
  for key,counts in val.items():
    if key in my_dict_sum :
        sum_dict[key] = list(map(lambda x,y: x y, my_dict_sum[key], counts))
    else:
        my_dict_sum[key] = counts

CodePudding user response:

you could do somthing like this:

from itertools import zip_longest
from collections import defaultdict
out = defaultdict(list)
for line in my_dict:
     for key, val in line.items():
        out[key] = list(map(sum, zip_longest(val, out[key], fillvalue=0)))

and results:

defaultdict(list,
            {'prop1': [3, 5, 6, 5],
             'prop2': [1, 1, 0, 0],
             'prop3': [1, 1, 0, 0]})

CodePudding user response:

EDIT:

Here is a horrific one-liner with no explicit loops. I'm not sure why you'd ever want to do this. You SHOULDN'T do this. Assuming you don't count comprehension as explicit loops despite the presence of the word "for".

from functools import reduce

output = {k: reduce(
    lambda a, b:
        map(lambda d, e:
            d e, a, b[k] if k in b else [0,0,0,0]
        ),
        dicts, 
        [0, 0, 0, 0]
    )
    for k in reduce(
        lambda s, b:
            s | set(b.keys()),
        dicts, set()
    )
}

Here's a simpler version with explicit loops, that I feel is a lot clearer than your code so maybe that's sufficient.

from collections import defaultdict

dicts = [
    {
      'prop1': [1, 2, 3, 4],
      'prop2': [1, 1, 0, 0]
    },
    {
      'prop1': [2, 3, 3, 1],
      'prop3': [1, 1, 0, 0]
    }
]

output = defaultdict(lambda: [0, 0, 0, 0])

for d in dicts:
    for k, l in d.items():
        for i, v in enumerate(l):
            output[k][i]  = v

Note that it will only work if all the lists have exactly four items. For handling the case where you don't know how many items are in the lists, you can do this slightly more complex version:

output = defaultdict(list)

for d in dicts:
    for k, l in d.items():
        for i, v in enumerate(l):
            if i >= len(output[k]):
                output[k].append(v)
            else:
                output[k][i]  = v

You could do this as a one-liner using reduce and map and a bunch of lambdas, but it would be an incomprehensible mess. If you're concerned about a bunch of loops inline in your code, just put it in a function and call the function.

  • Related