TrashMachine139
TrashMachine139

Reputation: 163

Using reduce on a list of dictionaries of dictionaries

Here is the given list.

   Pets = [{'f1': {'dogs': 2, 'cats': 3, 'fish': 1},
         'f2': {'dogs': 3, 'cats': 2}},
        {'f1': {'dogs': 5, 'cats': 2, 'fish': 3}}]

I need to use the map and reduce function so that I can have a final result of

{'dogs': 10, 'cats': 7, 'fish': 4}

I have written a function using map

def addDict(d):
    d2 = {}
    for outKey, inKey in d.items():
        for inVal in inKey:
            if inVal in d2:
                d2[inVal] += inKey[inVal]
            else:
                d2[inVal] = inKey[inVal]
    return d2


def addDictN(L):
    d2 = list(map(addDict, L))
    print(d2)

That returns

[{'dogs': 5, 'cats': 5, 'fish': 1}, {'dogs': 5, 'cats': 2, 'fish': 3}]

It combines the f1 and f2 of the first and second dictionaries, but I am unsure of how to use reduce on the dictionaries to get the final result.

Upvotes: 1

Views: 84

Answers (3)

Ajax1234
Ajax1234

Reputation: 71451

You can use purely map and reduce like so:

Pets = [{'f1': {'dogs': 2, 'cats': 3, 'fish': 1},
     'f2': {'dogs': 3, 'cats': 2}},
    {'f1': {'dogs': 5, 'cats': 2, 'fish': 3}}]

new_pets = reduce(lambda x, y:[b.items() for _, b in x.items()]+[b.items() for _, b in y.items()], Pets)
final_pets = dict(reduce(lambda x, y:map(lambda c:(c, dict(x).get(c, 0)+dict(y).get(c, 0)), ['dogs', 'cats', 'fish']), new_pets))

Output:

{'fish': 4, 'cats': 7, 'dogs': 10}

Upvotes: 0

David A
David A

Reputation: 413

Without using map and reduce, I would be inclined to do something like this:

from collections import defaultdict
result = defaultdict()
for fdict in pets:
    for f in fdict.keys():
        for pet, count in fdict[f].items():
            result[pet] += count

Using reduce (which really is not the right function for the job, and is not in Python 3) on your current progress would be something like this:

from collections import Counter
pets = [{'dogs': 5, 'cats': 5, 'fish': 1}, {'dogs': 5, 'cats': 2, 'fish': 3}]
result = reduce(lambda x, y: x + Counter(y), pets, Counter())

Upvotes: 1

jpp
jpp

Reputation: 164653

You can use collections.Counter to sum your list of counter dictionaries.

Moreover, your dictionary flattening logic can be optimised via itertools.chain.

from itertools import chain
from collections import Counter

Pets = [{'f1': {'dogs': 2, 'cats': 3, 'fish': 1},
         'f2': {'dogs': 3, 'cats': 2}},
        {'f1': {'dogs': 5, 'cats': 2, 'fish': 3}}]

lst = list(chain.from_iterable([i.values() for i in Pets]))

lst_sum = sum(map(Counter, lst), Counter())

# Counter({'cats': 7, 'dogs': 10, 'fish': 4})

This works for an arbitrary length list of dictionaries, with no key matching requirements across dictionaries.

The second parameter of sum is a start value. It is set to an empty Counter object to avoid TypeError.

Upvotes: 2

Related Questions