This question already has answers here:
How do I iterate through two lists in parallel?
(8 answers)
Closed 2 years ago.
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)):
...
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.
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)]
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
for (i,j) in [(i,j) for i in range(x) for j in range(y)]
should do it.
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')
…
"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]
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.
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
Related
I have 2 differently sized lists (a and b) and starting index for a (a_start). I would like to add the lists together, accounting for the fact they are not aligned.
Example inputs and expected output:
a = [1,2]
b = [1,2,3,4]
a_start = 1 # 1st element of a needs to be added to 2nd element of b, etc.
expected result: [1,3,5,4]
Another example:
a = [1,2,3,4]
b = [1,2,3,4]
a_start = 2 # 1st element of a needs to be added to 3rd element of b, etc.
expected result: [1, 2, 4, 6, 3, 4]
My current solution:
[i+j for i,j in zip([0]*a_start + a + [0]*(len(b) - len(a) - a_start), b+[0]*max(0,len(a) + a_start - len(b)))]
What is a more elegant/Pythonic way to do this for any arbitrarily sized lists?
It would probably be best to use zip_longest:
a_padded = a_start * [0] + a
result = [x + y for x, y in itertools.zip_longest(a_padded, b, fillvalue=0)]
Also, if space is an issue, you may want to avoid creating a copy of a. In that case:
from itertools import zip_longest, chain, repeat
a_padded = chain(repeat(0, a_start), a)
result = [x + y for x, y in zip_longest(a_padded, b, fillvalue=0)]
Since zip_longest is already taken, here's one like yours, but I compute the length difference only once (and as a separate line, for readability) and take advantage of Python treating negative list multipliers like zero (your max(0, ...) is unnecessary):
d = a_start + len(a) - len(b)
result = [*map(sum, zip([0]*a_start + a + [0]*-d, b + [0]*d))]
I don't get how a "for" loop that iterates through the elements of a list can be out of range.
First part seems to be okay as I can print it.
import random
def random_list(n):
l = []
for i in range(0,n):
l.append(random.randint(0,n))
return l
def maximum(n):
x = 0
b = random_list(n)
for i in b:
if b[i] > x:
x = b[i]
print (maximum(10))
This:
for i in b:
if b[i] > x:
x = b[i]
Iterates over the elements of b, not the indices. Change it to
for i in b:
if i > x:
x = i
You also need to return something from your function, probably x.
Considering that you know how to iterate over a list in python, the error could be due to randint, which generates an integer in the interval [low, high]. This means high is a possible output, while the highest index in your program is high - 1.
For example,
random.randint(0, 0)
gives 0.
Similarly, random.randint(10) can return 10.
If you don't understand how to iterate over a list in Python, consider a simple example:
Take the list below:
myList = [1, 3, 5, 7, 9]
Now, there are two ways to iterate over the list:
Directly accessing elements:
for element in myList:
print(element, end=" ")
This gives the output:
1 3 5 7 9
Accessing elements using indices
for idx in range(len(myList)):
print(idx, ":", myList[idx], end=" ")
This gives the output:
0:1 1:3 2:5 3:7 4:9
I did this code that finds two integers in a said list (in this case [2,4,5,1,6,40,-1]) that multiply to twenty. I got a little stuck in the beginning, but adding a function to it solved my problems. I showed this code to a friend of mine who's a programmer and he said I could make this code more "pythonic", but I have no clue how.
Here's the code:
num_list = [2,4,5,1,6,40,-1]
def get_mult_num(given_list):
for i in given_list:
for j in range(i+1, len(given_list)): #for j not to be == i and to be in the list
mult_two_numbers = i * j
if mult_two_numbers == 20:
return i,j
print(get_mult_num(num_list))
I don't necessarily think it is 'unpythonic', you are using standard Python idioms to loop over your data and produce a single result or None. The term Pythonic is nebulous, a subject marred in "I know it when I see it" parameters.
Not that you produced a correct implementation. While i loops over given_numbers, j loops over an integer from i + 2 through to len(given_numbers), mixing values from given_list with indices? For your sample input, you are taking j from the half-open ranges [4, 7), [6, 7), [7, 7) (empty), [3, 7), [8, 7) (empty), [42, 7) (empty) and [1, 7), respectively. That it produces the correct answer at all is luck, not due to correctness; if you give your function the list [2, 10], it'll not find a solution! You want to loop over given_numbers again, limited with slicing, or generate indices starting at the current index of i, but then your outer loop needs to add a enumerate() call too:
for ii, i in enumerate(given_numbers):
for j in given_numbers[ii + 1:]:
# ...
or
for ii, i in enumerate(given_numbers):
for jj in range(ii + 1, len(given_numbers)):
j = given_numbers[jj]
# ...
All this is not nearly as efficient as it can be; the Python standard library offers you the tools to generate your i, j pairs without a nested for loop or slicing or other forms of filtering.
Your double loop should generate combinations of the integer inputs, so use the itertools.combinations() object to generate unique i, j pairs:
from itertools import combinations
def get_mult_num(given_list):
return [(i, j) for i, j in combinations(given_list, 2) if i * j == 20]
This assumes there can be zero or more such solutions, not just a single solution.
If you only ever need the first result or None, you can use the next() function:
def get_mult_num(given_list):
multiplies_to_20 = (
(i, j) for i, j in combinations(given_list, 2)
if i * j == 20)
return next(multiplies_to_20, None)
Next, rather than produce all possible combinations, you may want to invert the problem. If you turn given_list into a set, you can trivially check if the target number 20 can be divided cleanly without remainder by any of your given numbers and where the result of the division is larger and is also an integer in the set of numbers. That gives you an answer in linear time.
You can further limit the search by dividing with numbers smaller than the square root of the target value, because you won't find a larger value to match in your input numbers (given a number n and it's square root s, by definition s * (s + 1) is going to be larger than n).
If we add an argument for the target number to the function and make it a generator function, then you get:
def gen_factors_for(target, numbers):
possible_j = set(numbers)
limit = abs(target) ** 0.5
for i in numbers:
if abs(i) < limit and target % i == 0:
j = target // i
if j in possible_j and abs(j) > abs(i):
yield i, j
This approach is a lot faster than testing all permutations, especially if you need to find all possible factors. Note that I made both functions generators here to even out the comparisons:
>>> import random, operator
>>> from timeit import Timer
>>> def gen_factors_for_division(target, numbers):
... possible_j = set(numbers)
... limit = abs(target) ** 0.5
... for i in numbers:
... if abs(i) < limit and target % i == 0:
... j = target // i
... if j in possible_j and abs(j) > abs(i):
... yield i, j
...
>>> def gen_factors_for_combinations(target, given_list):
... return ((i, j) for i, j in combinations(given_list, 2) if i * j == target)
...
>>> numbers = [random.randint(-10000, 10000) for _ in range(100)]
>>> targets = [operator.mul(*random.sample(set(numbers), 2)) for _ in range(5)]
>>> targets += [t + random.randint(1, 100) for t in targets] # add likely-to-be-unsolvable numbers
>>> for (label, t) in (('first match:', 'next({}, None)'), ('all matches:', 'list({})')):
... print(label)
... for f in (gen_factors_for_division, gen_factors_for_combinations):
... test = t.format('f(t, n)')
... timer = Timer(
... f"[{test} for t in ts]",
... 'from __main__ import targets as ts, numbers as n, f')
... count, total = timer.autorange()
... print(f"{f.__name__:>30}: {total / count * 1000:8.3f}ms")
...
first match:
gen_factors_for_division: 0.219ms
gen_factors_for_combinations: 4.664ms
all matches:
gen_factors_for_division: 0.259ms
gen_factors_for_combinations: 3.326ms
Note that I generate 10 different random targets, to try to avoid a lucky best-case-scenario hit for either approach.
[(i,j) for i in num_list for j in num_list if i<j and i*j==20]
This is my take on it, which uses enumerate:
def get_mult_num(given_list):
return [
item1, item2
for i, item1 in enumerate(given_list)
for item2 in given_list[:i]
if item1*item2 == 20
]
I think your friend may be hinting towards using comprehensions when it makes the code cleaner (sometimes it doesn't).
I can think of using list-comprehension. This also helps to find multiple such-pairs if they exist in the given list.
num_list = [2,4,5,1,6,40,-1]
mult_num = [(num_list[i],num_list[j]) for i in range(len(num_list)) for j in range(i+1, len(num_list)) if num_list[i]*num_list[j] == 20]
print mult_num
Output:
[(4, 5)]
I came up with this. It reverses the approach a little bit, in that it searches in num_list for the required pair partner that the iteration value val would multiply to 20 with. This makes the code easier and needs no imports, even if it's not the most efficient way.
for val in num_list:
if 20 / val in num_list:
print(val, int(20/val))
You could make it more pythonic by using itertools.combinations, instead of nested loops, to find all pairs of numbers. Not always, but often iterating over indices as in for i in range(len(L)): is less pythonic than directly iterating over values as in for v in L:.
Python also allows you to make your function into a generator via the yield keyword so that instead of just returning the first pair that multiplies to 20, you get every pair that does by iterating over the function call.
import itertools
def factors(x, numbers):
""" Generate all pairs in list of numbers that multiply to x.
"""
for a, b in itertools.combinations(numbers, 2):
if a * b == x:
yield (a, b)
numbers = [2, 4, 5, 1, 6, 40, -1]
for pair in factors(20, numbers):
print(pair)
For example, I have two lists:
A=['a','b','c']
B=['a','a','b','c']
I want my output to look like:
[2,1,1]
Here is what I tried:
P=np.ndarray(shape=3, dtype=int)
count=0
d=0
for i in A:
for j in B:
if i==j:
count+=1
P[d]=count
d+=1
But I am getting
[2,3,4]
as output. What is going wrong?
Just use count method:
A=['a','b','c']
B=['a','a','b','c']
[B.count(x) for x in A]
The methods as suggested by #CarlesMitjans or #WillenVanOnsem are better solutions than this, but the reason your method does not work is that you need to initialise your count variable inside the first loop (and also indent the d+=1 line):
P = np.ndarray(shape=3, dtype=int)
count = 0
d = 0
for i in A:
count = 0
for j in B:
if i == j:
count += 1
P[d] = count
d += 1
which gives:
>>> P
array([2, 1, 1])
If the elements are hashable, you can simply use a counter you then transform back into a list. Like:
from collections import Counter
ctr = Counter(B)
result = [ctr[x] for x in A]
This generates:
>>> [ctr[x] for x in A]
[2, 1, 1]
This works in O(|A|+|B|) with |A| the numbers in A and |B| the numbers in B (given dictionary lookup works in O(1), which is nearly always the case).
If the elements can not be hashed (for instance lists, dictionaries, objects,...), then you can use #CharlesMitjans answer, which runs in O(|A|×|B|) (which tends to be less efficient, but then you cannot take advantage of hashing).
I have an int list with unspecified number. I would like to find the difference between two integers in the list that match a certain value.
#Example of a list
intList = [3, 6, 2, 7, 1]
#This is what I have done so far
diffList = []
i = 0
while (i < len(intList)):
x = intList[i]
j = i +1
while (j < len(intList)):
y = intList[j]
diff = abs(x-y)
diffList.append(diff)
j += 1
i +=1
#Find all pairs that has a difference of 2
diff = diffList.count(2)
print diff
Is there a better way to do this?
EDIT: Made changes to the codes. This is what I was trying to do. What I want to know is what else can I use besides the loop.
seems like a job for itertools.combinations
from itertools import combinations
for a, b in combinations(intList, 2):
print abs(a - b)
You could even turn this one into a list comprehension if you wanted to :)
[abs(a -b) for a, b in combinations(intList, 2)]
int_list = [3, 6, 2, 7, 1]
for x in int_list:
for y in int_list:
print abs(x - y)