简体   繁体   中英

Condenseing/converting a nested list using dictionary comprehension

So I have data structured in a nested list like so

data = [['A', '1'], ['B', '2'], ['C', '3'], ['A', '-2'], ['B', '4'], ['C', '1'], ['A', '2'], ['B', '1'], ['C', '-5']]

and I am trying to convert it into an output that looks like this

{'A': 1, 'C': -1, 'B': 7}

Basically sum up all of the A's, B's, and C's, put output as a dictionary.

I wrote this code which gives the correct answer

playerSum = {}
for ele in data:
    if ele[0] not in playerSum:
        playerSum[ele[0]] = int(ele[1])
    else:
        playerSum[ele[0]] += int(ele[1])

However, I am trying to convert the code block above into dictionary comprehension. I understand mostly how to do it, but I don't understand how to write the += as a dictionary comprehension. Any guidance on the structure would be great.

So far I have this

playerSum = {ele[0]: int(ele[1]) if ele[0] not in playerSum else playerSum[ele[0]] += int(ele[1]) for ele in data}

Edit: So @achampion was able to solve it. Thanks!

{key: sum(int(v) for k, v in data if k==key) for key in set(k for k, _ in data)}

It is not practical to do it as a comprehension.
Just as an exercise you can use a coroutine to do the counting for you, but you effectively create the dictionary twice:

from collections import defaultdict
def count():
    cache = defaultdict(int)
    k, v = yield
    while True:
        cache[k] += v
        k, v = yield cache[k]

counter = count()  # Create coroutine
next(counter)      # Prime coroutine

data = [['A', '1'], ['B', '2'], ['C', '3'], ['A', '-2'], ['B', '4'],
        ['C', '1'], ['A', '2'], ['B', '1'], ['C', '-5']]

{k: counter.send((k, int(v))) for k, v in data}  # Meets the challenge :)

Result:

{'A': 1, 'B': 7, 'C': -1}

Or a truly ugly one-liner that doesn't need a coroutine and isn't quadratic (not a comprehension):

>>> reduce(lambda d, (k,v): d.__setitem__(k, d.get(k,0)+int(v)) or d, data, {})  # Py2.7
{'A': 1, 'B': 7, 'C': -1}

And finally a very inefficient but true dict comprehension based on @Prune:

>>> {key: sum(int(v) for k, v in data if k==key) for key in set(k for k, _ in data)}
{'A': 1, 'B': 7, 'C': -1}

The best way is the most obvious way

from collections import defaultdict
playerSum = defaultdict(int)

for key, value in data:
   playerSum[key] += int(value)

It is not possible to use a dict comprehension as your values would be overwritten, the dict does not get created until the comprehension is finished so even if you could there is nothing to +=. As it stands unless you have playerSum = {} somewhere your code will error with a NameError, if you do you are simply rebinding the name to the result of your dict comp so playerSum = {} is basically doing nothing.

The only way to do what you want is to along the lines of your own solution . For more efficient approach You can unpack the sublists and cast the second element to int, summing the value using a collections.defaultdict :

from collections import defaultdict

d = defaultdict(int)

for a,b  in data:
    d[a] += int(b)

print(d)
defaultdict(<type 'int'>, {'A': 1, 'C': -1, 'B': 7})

Or using a regular dict:

d = {}

for a, b in data:
    d[a] = d.get(a,0) + int(b)

print(d)
{'A': 1, 'C': -1, 'B': 7}

I did it in a single comprehension, as you asked.

dict ([(key, sum(int(elem[1]) for elem in data if elem[0]==key)) for key in [id for id in set([elem[0] for elem in data])] ])

From outer to inner:

  • Build a set of the ID's used in the list.

  • For each ID, make a list of the associated values.

  • Sum the list.

  • Emit (yield) the ID and sum as a pair.

  • Turn this list of tuples into a dictionary.

Test:

data = [['A', '1'], ['B', '2'], ['C', '3'],
        ['A', '-2'], ['B', '4'], ['C', '1'],
        ['A', '2'], ['B', '1'], ['C', '-5']]
playerSum = dict ([(key, sum(int(elem[1]) for elem in data if elem[0]==key))
                   for key in [id for id in set([elem[0] for elem in data])] ])
print data
print playerSum

Result:

[['A', '1'], ['B', '2'], ['C', '3'], ['A', '-2'], ['B', '4'], ['C', '1'], ['A', '2'], ['B', '1'], ['C', '-5']]
{'A': 1, 'C': -1, 'B': 7}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM