user236529
user236529

Reputation: 105

Iterating through 2 dimensional lists in 1 line

For a 2D list [["bacon", "banana"], ["ham", "salami", "cheese"]] I want to iterate through as "bacon", then "banana", then "ham" etc.

sandwiches = [["bacon", "banana"], ["ham", "salami", "cheese"]]
preferences = ["bacon", 5, "ham", -2, "salami", 1]

In the sandwiches array, there are 2 sandwiches, bacon+banana and ham+salami+cheese. I want to know the score for each sandwich by doing something like this (doesn't work but shows what I'd like to do):

sandwichscores = [0 for i in sandwiches]
sandwichscores = [+preferences[preferences.index(j)+1] for j in i in sandwiches if
j in preferences]

But of course for j in i in sandwiches doesn't work. I've tried messing around with the order of the fors and ins but haven't gotten anywhere. How could this be done?

For instance, the output I want here would be:

sandwichscores = [5, -1]

Since the ingredients have a combined score of 5 in the first sandwich and -1 in the second. The ingredients that aren't in the preferences get ignored.

Upvotes: 0

Views: 4963

Answers (5)

Vasilis G.
Vasilis G.

Reputation: 7859

Here is another way in which you can do it, although I changed the structure of preferences to dictionary, which is more practical and easier to utilize:

sandwiches = [["bacon", "banana"], ["ham", "salami", "cheese"]]
preferences = {"bacon" : 5, "ham" : -2, "salami" : 1}

scores = [sum(preferences.get(ingredient,0) for ingredient in sandwich) for sandwich in sandwiches]
print(scores)

Output:

[5, -1]

Upvotes: 1

Patrick Artner
Patrick Artner

Reputation: 51683

I would use a dictionary for the score lookup - it is the designated datastructure for lookups.

You can go from your representation of preferences to a dictionary with a dict-comprehension:

preferences = ["bacon", 5, "ham", -2, "salami", 1]

prefs = {k:v for k,v in zip(preferences[::2],preferences[1::2])} # dict-comprehension

This uses zip() to join every 2nd element starting at 0 ["bacon","ham","salami"] to every 2nd element starting at 1 [5,-2,1] resulting in a zip-generator of (("bacon,5),("ham",-2),("salami",1)).

The zip-generator-result is then decomposed into key:value pairs that create the dict.


With that out of the way, you can calculate your sums like so:

sandwiches = [["bacon", "banana"], ["ham", "salami", "cheese"]]
prefs = {"bacon": 5, "ham": -2, "salami": 1}

score = [ (sandw, sum( prefs.get(ingred,0) 
                       for ingred in sandw)) 
          for sandw in sandwiches ]

print(score) 

The score sums up each ingredients score for each sandwinch you defined. If an ingredient is not found, 0 is added instead.

The output is a tuple of the sandwich and its score:

 [(['bacon', 'banana'], 5), (['ham', 'salami', 'cheese'], -1)]

Upvotes: 0

iacob
iacob

Reputation: 24371

I think this is the logic you want to use, unpacked into a nested for loop:

for i, sandwich in enumerate(sandwiches):
    for filling in sandwich:
        if filling in preferences:
            sandwichscores[i] += preferences[preferences.index(filling)+1]

print(sandwichscores)
>>> [5, -1]

As i alarmed alien says, the logic is much simpler if you use a dictionary for the scores:

preferences = {'bacon':5,'ham':-2,'salami':1}

#...

for i, sandwich in enumerate(sandwiches):
    for filling in sandwich:
        sandwichscores[i] += preferences.get(filling, 0)

Upvotes: 1

i alarmed alien
i alarmed alien

Reputation: 9530

Here is a one-liner that does what you want; I converted your preferences list into a dictionary because that is a much more suitable data structure when you are storing key/value pairs:

sandwiches = [["bacon", "banana"], ["ham", "salami", "cheese"]]
prefs = {"bacon": 5, "ham": -2, "salami": 1}

scores = [ [ ", ".join(i), sum( prefs[j] for j in i if j in prefs) ] for i in sandwiches ]
print(scores)

Output:

[['bacon, banana', 5], ['ham, salami, cheese', -1]]

The solution uses sum to add up the values of the sandwich ingredients, which are filtered by whether or not the ingredient appears in prefs.

You can change the output format to remove the list of ingredients and just output the score by altering the [ ", ".join(i), sum( prefs[j] for j in i if j in prefs) ] part. I would have assumed it was important to know which sandwich got which score, but who knows!

Upvotes: 1

Andrej Kesely
Andrej Kesely

Reputation: 195613

Here's one liner using groupby:

from itertools import groupby

sandwiches = [["bacon", "banana"], ["ham", "salami", "cheese"]]
preferences = ["salami", 999, "bacon", 5, "ham", -2, "banana", 1000]

l = [(v, sum(i[-1] for i in g)) for v, g in groupby(((sandwich, preferences[preferences.index(p)+1]) for sandwich, prefs in zip(sandwiches, [preferences[::2]] * len(sandwiches)) for p in prefs if p in sandwich), key=lambda v: v[0])]
print(l)

Prints:

[(['bacon', 'banana'], 1005), (['ham', 'salami', 'cheese'], 997)]

For sandwich ['bacon', 'banana']: Bacon has 5, banana has 1000, sandwich score is 1005

For sandwich ['ham', 'salami', 'cheese']: Ham has -2, salami has 999, sanwich score is 997

Upvotes: 0

Related Questions