164

How can I include two variables in the same for loop?

t1 = [a list of integers, strings and lists]
t2 = [another list of integers, strings and lists]

def f(t):  #a function that will read lists "t1" and "t2" and return all elements that are identical
    for i in range(len(t1)) and for j in range(len(t2)):
        ...
martineau
  • 119,623
  • 25
  • 170
  • 301
Quester
  • 1,969
  • 4
  • 15
  • 13
  • Are x and y two lists? – CppLearner Sep 06 '13 at 01:51
  • 4
    @user2246674 `zip` is good iff the iterables have the same length. – kojiro Sep 06 '13 at 01:52
  • 6
    Do you want something like a nested loop in one line, or just to iterate over the lists simultaneously? – SethMMorton Sep 06 '13 at 01:53
  • 4
    In case SethMMorton's question isn't clear to you: If `x` and `y` are both 3, simultaneous (aka "lock-step" or "parallel") iteration would give you `0, 0` then `1, 1` then `2, 2`; nested iteration would give you `0, 0`, `0, 1`, `0, 2`, `1, 0`, `1, 1`, `1, 2`, `2, 0`, `2, 1`, `2, 2`. (Or maybe you even want something different from both? In that case, please explain what.) – abarnert Sep 06 '13 at 01:58
  • Also, if you want simultaneous iteration, what happen is `x` and `y` are different? For example, if they're 2 and 3, do you want `0, 0`, `1, 1`, `2, 2`? Or `0, 0`, `1, 1`, `2, 2`, `None, 3`? Or…? – abarnert Sep 06 '13 at 02:01
  • why was this question voted down? – Quester Sep 06 '13 at 02:05
  • The constructive and healthy debate that the question sparked is most informative and beneficial. I actually learned a lot from it. So why the vote down? – Quester Sep 06 '13 at 02:06
  • 3
    I didn't downvote, but probably because it's not clear if you want simultaneous looping or nested looping, even after the commenters asked for clarification. – SethMMorton Sep 06 '13 at 02:06
  • @Quester: Agreed with SethMMorton. Even if you actually want to know the answers to _both_ questions (or to all possible variations of each), it's probably better to edit the question to say so explicitly, instead of to leave it ambiguous. (Most likely whoever downvoted isn't bothering to read anymore, so it won't get the vote undone… but it might get you one or more upvotes to counteract it.) – abarnert Sep 06 '13 at 02:09
  • You know, there's [an easier way](http://docs.python.org/3.3/library/stdtypes.html?highlight=frozenset#set) to get the identical members of two iterables. – kojiro Sep 06 '13 at 02:23
  • Do we safely assume lists `t1, t2` are guaranteed to have the same length, or else which is longer, and what behavior do you expect if we run off the end of the shorter list? Raise an IndexError, fill with a value (e.g. NAs, or sentinel), vector recycling...? – smci May 08 '19 at 02:17

8 Answers8

258

If you want the effect of a nested for loop, use:

import itertools
for i, j in itertools.product(range(x), range(y)):
    # Stuff...

If you just want to loop simultaneously, use:

for i, j in zip(range(x), range(y)):
    # Stuff...

Note that if x and y are not the same length, zip will truncate to the shortest list. As @abarnert pointed out, if you don't want to truncate to the shortest list, you could use itertools.zip_longest.

UPDATE

Based on the request for "a function that will read lists "t1" and "t2" and return all elements that are identical", I don't think the OP wants zip or product. I think they want a set:

def equal_elements(t1, t2):
    return list(set(t1).intersection(set(t2)))
    # You could also do
    # return list(set(t1) & set(t2))

The intersection method of a set will return all the elements common to it and another set (Note that if your lists contains other lists, you might want to convert the inner lists to tuples first so that they are hashable; otherwise the call to set will fail.). The list function then turns the set back into a list.

UPDATE 2

OR, the OP might want elements that are identical in the same position in the lists. In this case, zip would be most appropriate, and the fact that it truncates to the shortest list is what you would want (since it is impossible for there to be the same element at index 9 when one of the lists is only 5 elements long). If that is what you want, go with this:

def equal_elements(t1, t2):
    return [x for x, y in zip(t1, t2) if x == y]

This will return a list containing only the elements that are the same and in the same position in the lists.

SethMMorton
  • 45,752
  • 12
  • 65
  • 86
  • 4
    -1 The `zip` answer is underqualified. As I commented above, it only works if `x == y` here. – kojiro Sep 06 '13 at 01:57
  • 2
    @kojiro: Well, the OP didn't specify what should happen if `x != y`. Once he explains it, the answer is either going to be `zip` or `itertools.zip_longest`. – abarnert Sep 06 '13 at 02:00
  • is there a simultaneous iteration without zip? – Quester Sep 06 '13 at 02:03
  • @Quester: Sure. The documentation for [`zip`](http://docs.python.org/dev/library/functions.html#zip) and [`zip_longest`](http://docs.python.org/dev/library/itertools.html#itertools.zip_longest) even show you how to write exact equivalents of those functions. But usually it's the simplest way to do it. – abarnert Sep 06 '13 at 02:05
  • 3
    @Quester You could use `itertools.izip`, which is essentially the same thing. Other than that, not that I know of. But what's wrong with `zip`? – SethMMorton Sep 06 '13 at 02:05
  • It's a little better, but not that much. The hint is that OP uses `range` for both `x` and `y`, so it seems unlikely they're the same length. `izip_longest` is more interesting, but I'm not convinced it's right either. Anyway, I removed my downvote. – kojiro Sep 06 '13 at 02:06
  • @Quester: Obviously the equivalents in the docs are over-complicated for simple use cases, because they have to be fully general. But the idea is that you can always rewrite any loop around any combination of explicit `next` calls. – abarnert Sep 06 '13 at 02:07
  • @Seth: since Kojiro explained that zip will not work for two different values of x and y. – Quester Sep 06 '13 at 02:17
  • @Quester `zip` works for two different x and y, you just have to know how it behaves (as explained in my answer). However, based on your edit I'm not sure you want `zip`. – SethMMorton Sep 06 '13 at 02:35
  • @SethMMorton, Is there a way to do what you did in `product` without the iteration tools? – Royi Nov 18 '17 at 18:08
  • @Royi Not without writing your own complicated function. Can I ask why you wouldn't want to use `itertools`? It's part of the standard library. – SethMMorton Nov 18 '17 at 18:27
  • Just was curious. I will use it as you suggest. – Royi Nov 18 '17 at 18:50
99

There's two possible questions here: how can you iterate over those variables simultaneously, or how can you loop over their combination.

Fortunately, there's simple answers to both. First case, you want to use zip.

x = [1, 2, 3]
y = [4, 5, 6]

for i, j in zip(x, y):
   print(str(i) + " / " + str(j))

will output

1 / 4
2 / 5
3 / 6

Remember that you can put any iterable in zip, so you could just as easily write your exmple like:

for i, j in zip(range(x), range(y)):
    # do work here.

Actually, just realised that won't work. It would only iterate until the smaller range ran out. In which case, it sounds like you want to iterate over the combination of loops.

In the other case, you just want a nested loop.

for i in x:
    for j in y:
        print(str(i) + " / " + str(j))

gives you

1 / 4
1 / 5
1 / 6
2 / 4
2 / 5
...

You can also do this as a list comprehension.

[str(i) + " / " + str(j) for i in range(x) for j in range(y)]
starball
  • 20,030
  • 7
  • 43
  • 238
Morgan Harris
  • 2,589
  • 14
  • 17
20

Any reason you can't use a nested for loop?

for i in range(x):
   for j in range(y):
       #code that uses i and j
kale
  • 151
  • 1
  • 10
Jags
  • 1,639
  • 1
  • 16
  • 30
  • 11
    @Matt: it just didn't look elegant enough to me. – Quester Sep 06 '13 at 01:58
  • 7
    @Quester: You can use `product` instead, as in SethMMorton's answer. That would certainly be more elegant/readable if you had six ranges instead of 2, or the number of them were dynamic and only known at runtime. But for just 2 of them… it's hard to beat 2 nested loops for simplicity. – abarnert Sep 06 '13 at 02:03
  • And who doesn't love going through indenting everything to make python happy? – mohbandy Sep 20 '16 at 20:06
  • 5
    nested loop runs for i*j times. what if he wanted to run it for i times only, considering i == j – QuestionEverything Dec 20 '16 at 15:37
  • @abarnert "> it's hard to beat 2 nested loops for simplicity." I was working on a project which required me to type hundereds of times "`for i in range(len(...)): for j in rnage(len((...))`" ... a lot of time would have been saved had I known this and then i could do "`for (i,j) in grid:`" – Kartik Dec 14 '17 at 16:03
18
for (i,j) in [(i,j) for i in range(x) for j in range(y)]

should do it.

qaphla
  • 4,707
  • 3
  • 20
  • 31
9

If you really just have lock-step iteration over a range, you can do it one of several ways:

for i in range(x):
  j = i
  …
# or
for i, j in enumerate(range(x)):
  …
# or
for i, j in ((i,i) for i in range(x)):
  …

All of the above are equivalent to for i, j in zip(range(x), range(y)) if x <= y.

If you want a nested loop and you only have two iterables, just use a nested loop:

for i in range(x):
  for i in range(y):
    …

If you have more than two iterables, use itertools.product.

Finally, if you want lock-step iteration up to x and then to continue to y, you have to decide what the rest of the x values should be.

for i, j in itertools.zip_longest(range(x), range(y), fillvalue=float('nan')):
  …
# or
for i in range(min(x,y)):
  j = i
  …
for i in range(min(x,y), max(x,y)):
  j = float('nan')
  …
kojiro
  • 74,557
  • 19
  • 143
  • 201
8

"Python 3."

Add 2 vars with for loop using zip and range; Returning a list.

Note: Will only run till smallest range ends.

>>>a=[g+h for g,h in zip(range(10), range(10))]
>>>a
>>>[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
Open-Business
  • 79
  • 1
  • 2
6

For your use case, it may be easier to utilize a while loop.

t1 = [137, 42]
t2 = ["Hello", "world"]

i = 0
j = 0
while i < len(t1) and j < len(t2):
    print t1[i], t2[j]
    i += 1
    j += 1

# 137 Hello
# 42 world

As a caveat, this approach will truncate to the length of your shortest list.

Daniel
  • 2,345
  • 4
  • 19
  • 36
3

I think you are looking for nested loops.

Example (based on your edit):

t1=[1,2,'Hello',(1,2),999,1.23]
t2=[1,'Hello',(1,2),999]

t3=[]

for it1, e1 in enumerate(t1):
    for it2, e2 in enumerate(t2):
        if e1==e2:
            t3.append((it1,it2,e1))

# t3=[(0, 0, 1), (2, 1, 'Hello'), (3, 2, (1, 2)), (4, 3, 999)]

Which can be reduced to a single comprehension:

[(it1,it2,e1) for it1, e1 in enumerate(t1) for it2, e2 in enumerate(t2) if e1==e2] 

But to find the common elements, you can just do:

print set(t1) & set(t2)
# set([(1, 2), 1, 'Hello', 999])

If your list contains non-hashable objects (like other lists, dicts) use a frozen set:

from collections import Iterable
s1=set(frozenset(e1) if isinstance(e1,Iterable) else e1 for e1 in t1)
s2=set(frozenset(e2) if isinstance(e2,Iterable) else e2 for e2 in t2)
print s1 & s2
dawg
  • 98,345
  • 23
  • 131
  • 206