23

Given structured data like [('a', 1), ('b', 3), ('c', 2)], how can I sum the integers (here, sum 1 + 3 + 2 to get 6) using the sum builtin, in a single expression?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
David Silva
  • 1,939
  • 7
  • 28
  • 60
  • Since OP already knew how to use `sum`, the question is really about how to *get data that can be summed* in a sub-expression passed to `sum`. This boils down to "how do I do something with each element of a list, and collect the results?", which is **extremely** commonly asked, but in **many** different forms. The closest canonical I have so far is [Apply function to each element of a list](/questions/25082410), but I am strongly considering doing a new canonical from scratch. – Karl Knechtel Feb 14 '23 at 17:17

4 Answers4

38
sum(n for _, n in structure)

would work.

David Robinson
  • 77,383
  • 16
  • 167
  • 187
  • 1
    Surprisingly enough, this is marginally faster than my solution (on python 2.7, OS-X 10.5). -- using a list comprehension increases the speed here by almost 50% (for this small example). – mgilson Aug 31 '12 at 15:38
  • 1
    The only weakness I can think of is that it won't work if one element happens to be `('a', 3, 'extra-data-to-ignore')`, whereas both the explicit index and the `zip` approaches -- even the `reduce` -- generalize a little better that way. I guess in Py3 you could write `sum(b for _, b, *_ in structure)` or something.. – DSM Aug 31 '12 at 15:40
  • @mgilson: took the words out of my mouth, I'd just run the tests myself (Python 2.7, OS-X 10.7). I've read before about how fast tuple unpacking is but it was interesting to see it in action. Might have to do with bound checking in the `x[1]` evaluation. – David Robinson Aug 31 '12 at 15:40
  • FWIW, I'd write this as `sum(n for _, n in structure)` to indicate the first item of each pair wasn't being used. – martineau Aug 31 '12 at 15:41
  • @martineau: I like it. It does look a little obscure, though (like the `_` is a special operator). – David Robinson Aug 31 '12 at 15:42
  • @DavidRobinson -- using `_` for unused elements when unpacking is a pretty well established python convention I think. – mgilson Aug 31 '12 at 15:43
  • 2
    It's a fairly common Python idiom though which many would recognize immediately. – martineau Aug 31 '12 at 15:43
  • @mgilson: I meant for beginners seeing this. If I were a beginner I'd wonder if `_,` were a special operator. – David Robinson Aug 31 '12 at 15:44
  • I've noticed before that `_` for "don't care variables" is a bit controversial. Beginners coming from Prolog or Haskell should be used to it, though :) – Fred Foo Aug 31 '12 at 15:49
  • @DSM: To avoid problems with extra data, one could write `import operator` then `sum(operator.itemgetter(1)(t) for t in structure)`. – martineau Aug 31 '12 at 15:49
  • @DavidRobinson: how can bounds checking matter here? `x, y = xy` does have to check whether `len(xy) == 2`, since otherwise it raises a `ValueError`. – Fred Foo Aug 31 '12 at 15:50
  • 3
    @martineau: you definitely could, but in this case that seems like a *very* roundabout way of writing `t[1]`.. – DSM Aug 31 '12 at 15:51
  • Prefer this answer to the others as it avoids using magic numbers. – mRyan Oct 30 '20 at 09:53
  • For anyone who is new to Python (like me) and is wondering why this works: the sum contains one generator expression (not two arguments, this initially confused me). It is very similar to the answer by mgilson. You can find more about generator expressions here: https://www.geeksforgeeks.org/generator-expressions/ – metatron Nov 09 '22 at 14:44
  • This way is still slightly faster in 3.8; using a list comprehension instead of a generator expression is also still faster, though not by as much in my testing as @mgilson's. (My test data uses 10,000 pairs of (single-character string, single-digit integer).) – Karl Knechtel Feb 14 '23 at 17:26
20
sum(x[1] for x in structure)

should work

mgilson
  • 300,191
  • 65
  • 633
  • 696
2

You could do

sum(zip(*structure)[1])
nbro
  • 15,395
  • 32
  • 113
  • 196
Eric
  • 95,302
  • 53
  • 242
  • 374
  • 1
    You could use `sum(map(operator.itemgetter(1),structure)` if you really want to avoid the comprehensions ... (but what's the point really?) – mgilson Aug 31 '12 at 15:17
  • 1
    @DavidRobinson -- This new version with `zip` is marginally slower than our answers on python2.7 (OS-X 10.5). – mgilson Aug 31 '12 at 15:47
  • @mgilson in 3.x, `zip` produces an iterable which must be manually unpacked before the `sum`. There aren't any elegant ways to make a single expression out of it (although of course many things can be put on one *line* using `;`) and this approach is *considerably* slower now (unpacking in a list comprehension is nearly twice as fast in my tests). – Karl Knechtel Feb 14 '23 at 17:26
1

Using a functional style, you could do

reduce(lambda x,y:x+y[1], structure,0)
nbro
  • 15,395
  • 32
  • 113
  • 196
John Wang
  • 4,562
  • 9
  • 37
  • 54
  • 1
    Just a hint, functional programming methods almost got removed from `Python 3` reduce isn't event loaded by default anymore. So list comprehensions should be preferred in this case – user1767754 Dec 03 '17 at 06:56
  • Aside from that, OP *specifically* asked to use `sum`. – Karl Knechtel Feb 14 '23 at 17:18