# Introduction

I think ** multiple for loop (nesting) ** is one of the most avoidable processes in programming. About a month ago, the article "Why do we stubbornly avoid for" was buzzed, and 2020/11/3 With more than 1000 LGTMs now, many people don't want to use for loops too much. There are several techniques in Python that can easily avoid multiple for loops, so I've summarized the ones I use most often. If you have any other, please let us know in the comments.

# Why do you want to avoid multiple for loops in the first place?

I think there are various reasons for each person (also discussed in detail in the above article), but personally I have the following two points.

As an example of multiple for loops, let's write a program that searches all 0 to 999 by turning a for statement from 0 to 9 for each of the 3 digits, and searches for numbers that are 500 or more and doublet. (suitable) Even just a triple for loop will make you sick.

#### `bad example`

``````

result = []
for i in range(10):
for j in range(10):
for k in range(10):
summed = i*100+j*10+k
if i == j == k and summed >= 500:
result.append(summed)
print(result)

#Execution result-> [555, 666, 777, 888, 999]
``````

It's still good because the process is simple, but it gets really hard to read when it gets more complicated.

### 2. The process of exiting loops at once is very difficult to read.

Often, when you meet certain conditions, you want to get out of all the loops, not just the innermost loop. As an example, write a program that ends the process if even one doublet is found in the process of 1. above. When I search for "how to get out of python multiple loops" on Google, although there is a difference that flag is not used, it is written as follows.

#### `There is a bad example break`

``````

result = []
for i in range(10):
for j in range(10):
for k in range(10):
summed = i*100 + j*10 + k
if i == j == k and summed >= 500:
result.append(summed)
break
else:
continue
break
else:
continue
break

print(result)

#Execution result-> [555]
``````

This is pretty hard to read. It's very hard to tell where and where the indentation is the same. If you try to write more complex processes, you'll end up with code that no one can understand.

# Method A. Use itertools

By using itertools, you can generate iterators that produce all combinations and avoid multiple for loops. Also, since iterators are generated, even if you make all the combinations, it will not consume a lot of memory. If you want to list all the combinations you have created, just enclose the iterator created by the built-in function list (). (Be careful about memory consumption when listing)

When I write the code that breaks out of the loop using itertools,

#### `When using itertools`

``````
import itertools

all_nums_iter = itertools.product(range(10),repeat=3)

result = []
for num in all_nums_iter:
summed = num[0]*100 + num[1]*10 + num[2]
if num[0] == num[1] and num[1] == num[2] and summed >= 500:
result.append(summed)
break
print(result)

#Execution result-> [555]
``````

I think it's easier to see. By the way, itertools has a function that can not only enumerate all, but also enumerate combinations and permutations with and without duplication.

# Method B. Use numpy.npindex

If you're already using a numpy array, you can use numpy.npindex to avoid multiple for loops. numpy.npindex turns the for statement so that all the values in the received array are brute force. In the code below, options.shape is (10, 10, 10), so i, j, and k are full search for statements from 0 to 9 respectively.

#### `numpy.When using npindex`

``````
import numpy as np

options = np.array([[[100*i+10*j+k for i in range(10)] for j in range(10)] for k in range(10)], dtype="int")

result = []
for i, j, k in np.ndindex(options.shape):
summed = options[i,j,k]
if i == j and j == k and summed >= 500:
result.append(summed)
break

print(result)

#Execution result-> [555]
``````

It's pretty refreshing.

# Method C. Use recursive function

It's a little difficult and I think it's used in a limited number of situations, but you can also do a full search with a recursive function. If you are not familiar with recursive functions, see "What kind of world will expand if you learn recursive functions" in the reference below. I think it's pretty easy to understand.

#### `When using a recursive function`

``````
def dfs(depth, num):
if depth == 3:
num_str = str(num).zfill(3)
return num if num_str[0] == num_str[1] == num_str[2] and num >= 500 else None

for i in range(10):
ret = dfs(depth + 1, num + 10**(2-depth) * i)
if ret:
return ret

print(dfs(0, 0))

#Execution result-> 555
``````

You can use it in a limited number of situations, but you can write intuitive code in specific situations (DFS: depth-first search, etc.).