2

I have a list of lists named allBins which contains several lists which represent different bins, inside those bins are varying numbers of tuples with the format (iD, volume). I need to iterate through to sum the volumes of the items in each of the bins by summing the second element of the tuples.

I've tried many things: sum(bin[1] for bin in allBins) gives me a 'list index out of range' error presumably because some bins have more than one tuple?

allBins = [[(3,20)],[(1,11),(0,6)],[(4,16),(2,5)]]

I need a line of code that, depending on which bin I choose to sum, gives me the following integers:

1st bin: 20

2nd bin: 17

3rd bin: 21

Jacob Myer
  • 479
  • 5
  • 22
  • `[sum(l[1] for l in sublst) for sublst in allBins]` – Chris_Rands Feb 18 '19 at 16:45
  • what you've posted returns a list with the sums that I want but what I'm looking for is a single integer which is the sum of a particular bin. I wasn't clear originally, edited my question to reflect this – Jacob Myer Feb 18 '19 at 17:37

6 Answers6

5

You can use a list-comprehension:

allBins = [[(3,20)],[(1,11),(0,6)],[(4,16),(2,5)]]

print([sum(y[1] for y in x) for x in allBins])
# [20, 17, 21]

Treating your actual requirement: "I need some sort of loop or comprehension that, depending on which bin I choose to sum":

allBins = [[(3,20)],[(1,11),(0,6)],[(4,16),(2,5)]]

bin_number = 2
print(sum(x[1] for x in allBins[bin_number-1]))
# 17

You can specify bin_number and the above finds sum for that particular bin.

Austin
  • 25,759
  • 4
  • 25
  • 48
  • Your first example says to use a list comprehension, but then you use a generator comprehension to address the actual requirement. – munk Feb 18 '19 at 17:54
  • @munk, is that the reason for a down vote? OP is asking for a comprehension any way. – Austin Feb 18 '19 at 17:56
  • It is. I'm not saying you're not answering the question, but it's subtly misleading. I'm imagining someone new coming along and seeing the two solutions with the different syntax and being confused. If you were to clarify that and explain why you're using one instead of the other, I'd change that downvote to an upvote. – munk Feb 18 '19 at 17:59
  • @munk, agree! Maybe I've missed to explain how one differs from another in elaborate manner. Someone as you say who is likely to confuse can always use the *comment* feature to ask for clarifications. Anyway, thanks for tossing up. – Austin Feb 18 '19 at 18:02
2

The problem you have is you want to only sum one bin, but you're trying to sum across all bins. This means when you access the first bin, with the value [(3,20)] and ask for element with index 1, there is only an element with index 0 and you get the out of bounds error.

You want something like this:

def sum_bin(bin_num, data):
    my_bin = data[bin_num]
    return sum(t[1] for t in my_bin)

>>> sum_bin(0, allBins)
20
>>> sum_bin(1, allBins)
17
>>> sum_bin(2, allBins)
21

as a "one liner", assuming you have a variable capturing the bin you want

sum(t[1] for t in allBins[bin_idx])

This is called a generator comprehension, and while it's similar to a list comprehension, there are subtle differences.

munk
  • 12,340
  • 8
  • 51
  • 71
  • thank you, your 'one liner' works when I apply it to the `allBins` list that I have in my post. I didn't include it in the question because I thought it was unnecessary but what I am actually trying to do is define a first fit decreasing bin packing function. The sum calculated from your answer I was going to use to determine if the bin is full. When I put your one-liner and later call the function it gives me: 'TypeError: list indices must be integers or slices, not tuple'. Any ideas why this is? – Jacob Myer Feb 18 '19 at 18:19
  • Can you share how you’re generating that index? – munk Feb 18 '19 at 18:24
  • The entire function won't fit in the comments so it may warrant a new post, but I receive an error here on the line with the if statement The entire function won't fit in the comments so it may warrant a new post, but I receive an error here on the line with the if statement `allBins = [items[0]] for item in items: for bin in bins: if sum(i[1] for i in bins[bin]) + item[1] <= bin_cap:` – Jacob Myer Feb 18 '19 at 18:33
  • I think `bin` in this case is the actual bin, not the index to it. This means you don't have to use the index of the bin. The code should be changed in your example to `sum(i[1] for i in bin)` – munk Feb 18 '19 at 18:37
  • you're correct and I removed the index, but now the error I receive when I call the function is: `TypeError: 'int' object is not subscriptable` on that same line – Jacob Myer Feb 18 '19 at 18:41
  • can you print the content of `bin` just before that error happens and share that? – munk Feb 18 '19 at 18:44
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/188629/discussion-between-munk-and-jacob-myer). – munk Feb 18 '19 at 18:47
  • `items = [(2, 22), (0, 20), (1, 13)] bins = [items[0]]` therefore the content of `bin` before that error happens should be `(2,22)` – Jacob Myer Feb 18 '19 at 18:49
2

You were close :-) Just put the summing fragment you gave inside a list comprehension so that it runs one summation per bin.

FWIW, you can also use operator.itemgetter() for a beautiful, functional approach:

>>> from operator import itemgetter

>>> allBins = [[(3,20)],[(1,11),(0,6)],[(4,16),(2,5)]]
>>> [sum(map(itemgetter(1), bin)) for bin in allBins]
[20, 17, 21]

Read this as, "make a list of sums for every bin in all bins" where the sums are "sum of item one in each tuple in a bin".

Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
2

Once iterating over the main list, you can use sum to add up the integers.

allBins = [[(3,20)],[(1,11),(0,6)],[(4,16),(2,5)]]

def sumup(which, allBins):
    return sum(tup[1] for tup in allBins[which])

print(sumup(1, allBins))

Doc: sum built-in

Mark
  • 5,994
  • 5
  • 42
  • 55
0
for c,b in enumerate(allBins):
    if c == bin_you_choose_to_sum:
        partial=0
        for t in b:
            partial+=t[1]
        print("Bin {}: {}".format(c, partial))
Luca Giorgi
  • 910
  • 3
  • 14
  • 28
0

You can use the following function:

from operator import itemgetter

allBins = [[(3,20)],[(1,11),(0,6)],[(4,16),(2,5)]]

def func(bin_num, all_bins):
    bin = itemgetter(bin_num)(all_bins)
    s = sum(map(itemgetter(-1), bin))
    return s

print(func(2, allBins))
# 21
Mykola Zotko
  • 15,583
  • 3
  • 71
  • 73
  • 1
    You don't address the key part of the question. "depending on which bin I choose to sum". You sum all of them. The post you reference has the same problem and is also downvoted by me. – munk Feb 18 '19 at 18:38