1

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.

jpp
  • 159,742
  • 34
  • 281
  • 339
  • 1
    Any particular reason why you have to use `map` and `reduce`? – David A Mar 11 '18 at 02:00
  • @DavidA Its just the requirements for this particular function. I understand how to use the map function, and I think I understand reduce, but I have no idea how to run it with dictionaries – TrashMachine139 Mar 11 '18 at 02:04

3 Answers3

2

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.

jpp
  • 159,742
  • 34
  • 281
  • 339
  • hello, can i ask what is the role of of `map(counter,lst)` ? it doesnt seem to be callable function. – Abr001am Mar 11 '18 at 02:25
  • Sure, it's actually `map(Counter, lst)`. It applies `Counter` to every element of `lst`. It is an iterator, so you need to apply another function to it to see its output, e.g. `list` or `sum`. – jpp Mar 11 '18 at 02:26
  • 1
    Ah, so it's an iterator object like `itertools.combinations` it doesn't execute until you list or evaluate it, thanks for the tip. – Abr001am Mar 11 '18 at 02:28
1

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())
David A
  • 415
  • 1
  • 4
  • 13
0

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}
Ajax1234
  • 69,937
  • 8
  • 61
  • 102