Double Looping in python - python

Please help me with this.
For example, if I have a = [2, 5, 8, 4] and b = [1, 3, 6, 9].
how would I used "for" loop to pick an element in 'a' and its corresponding element in 'b' to use in other function? For instance, pick 2 in 'a' and pick 1 in 'b' then next pick 5 in 'a' and 3 in 'b'.

What you want is the zip() function:
Make an iterator that aggregates elements from each of the iterables.
You can use it like this:
a = [2, 5, 8, 4]
b = [1, 3, 6, 9]
def another_function(x, y):
print(x, y)
for item_a, item_b in zip(a, b):
another_function(item_a, item_b)
You get:
(2, 1)
(5, 3)
(8, 6)
(4, 9)
You can also use map() function:
Return an iterator that applies function to every item of iterable, yielding the results.
Your function should return a value:
def another_function(x, y):
return x, y
result = map(another_function, a, b)
for item in result:
print(item)
You get the same result.

The situation you are facing is called "Parallel Arrays". Wikipedia has a great explanation and example code.

If you know the lists are the same length, just use zip:
for a_elem, b_elem in zip(a, b):
# do stuff with the elements
If your lists might be different lengths, zip will give you a sequence the length of your shortest iterable. Use itertools.izip_longest if you need a sequence the length of your longest iterable.

Can't you just access one element after another using the same position in one for loop defining a function?
def access_A_and_B(a,b,number)
for i in range(len(a)): #itterate through a until you find your number
if(a[i] == number and len(b) > i): # if a at position i is your number
return b[i] #ask b what the corresponding value is and return it
return -1 #default value if number not in a
number: is the number you want to search

for x, y in zip(a, b):
some_func(x, y)

Related

How to iterate through a list without including the same element twice?

I was wondering how I could iterate through this list without including the same number twice.
import itertools
def sum_pairs(ints, s):
indexes = []
pair = []
for numbers in itertools.combinations(ints,2):
if sum(numbers) == s:
pair.append(numbers)
for n in numbers:
indexes.append(ints.index(n))
print(pair)
print(indexes)
a = [10, 5, 2, 3, 7, 5]
target = 10
Here's the output:
[(5, 5), (3, 7)]
[1, 1, 3, 4]
'pair' correctly outputs 5 and 5 to equal 10, but when I check where the numbers come from with the variable 'indexes', I can see that the same 5 was used twice and the second five was never taken into consideration. What i'm looking for is how can I modify this to not add the same number twice if it's in the same index. For ex. the output of indexes would be [1, 5, 3, 4].
Thank you so much.
Smuggle the index of each value along with it by using enumerate:
import itertools
def sum_pairs(ints, s):
indexes = []
pair = []
for (ix, x), (iy, y) in itertools.combinations(enumerate(ints),2):
if x + y == s:
pair.append((x, y))
indexes += (ix, iy)
print(pair)
print(indexes)
a = [10, 5, 2, 3, 7, 5]
target = 10
sum_pairs(a, target)
which outputs:
[(5, 5), (3, 7)]
[1, 5, 3, 4]
To simplify the usage of the values, I unpacked the tuple of tuples to names (x and y are the "real" values, ix is the index of x and iy is the index of y). By attaching the index to the value, you always know exactly where it came from, without having to guess at it.
Your use of the index method didn't work because, for all practical purposes, the two 5 in your input are indistinguishable (on CPython, thanks to the small int optimization, they're actually the same object), and index just returns the first one it finds (and has to needlessly rescan for it every time). By keeping the index with the associated value, you don't have to recheck at all, you already know it.
Run combination on the index instead. BTW your indexes is defined not-so-commonly. If you got what I meant, try change the 'extend' with an append below
def sum_pairs(ints, s):
indexes = []
pair = []
for numbers in itertools.combinations(range(len(ints)),2):
if ints[numbers[0]]+ints[numbers[1]] == s:
indexes.extend(numbers)
pair.append((ints[numbers[0]],ints[numbers[1]]))
print(pair)
print(indexes)

How to use functional programming to iterate and find maximum product of five consecutive numbers in a list?

I have to use functional programming to implement the following function takes in a list of numbers from 0 to 9. The goal is to find the five consecutive elements of the list that have the greatest product. The function should return tuple of the index of the greatest product and the value of the greatest product without using the max function.
I can easily implement this without functional programming but I am having trouble implementing it without any loops.
This is my approach so far but the part that I am stuck on is how to loop through the array to find those consecutive five numbers without loops. I am trying to use map to do that but I don't think it is correct. Is it possible to incorporate enumerate in any way? Any help is appreciated.
def find_products(L):
val = map(lambda a: reduce(lambda x,y: x*y, L),L)
print (val)
This doesn't have any explicit loops or call the max function. The function assumes that there're at least five elements in the input list and outputs a tuple (start_index, max_product).
from functools import reduce, partial
import operator
def f(l):
win = zip(l, l[1:], l[2:], l[3:], l[4:])
products = map(partial(reduce, operator.mul), win)
return reduce(lambda x, y: x if x[1] > y[1] else y, enumerate(products))
In [2]: f([1, 2, 3, 4, 7, 8, 9])
Out[2]: (2, 6048)
In [3]: f([2, 6, 7, 9, 1, 4, 3, 5, 6, 1, 2, 4])
Out[3]: (1, 1512)
win = zip(l, l[1:], l[2:], l[3:], l[4:]) creates a sliding window iterator of size 5 over the input list. products = map(partial(reduce, operator.mul), win) is an iterator calling partial(reduce, operator.mul) (translates to reduce(operator.mul, ...)) on every element of win. reduce(lambda x, y: x if x[1] > y[1] else y, enumerate(products)) adds a counter to products and returns the index-value pair with the highest value.
If you need a more general version and/or the input list is large you'd use itertools.islice:
from itertools import islice
def f(l, n=5):
win = zip(*(islice(l, i, None) for i in range(n)))
...
The code above uses a generator expression which is a loop, technically. A pure functional version of that might look like
from itertools import islice
def f(l, n=5):
win = zip(*map(lambda i: islice(l, i, None), range(n)))
...
from functools import reduce #only for python3, python2 doesn't need import
def find_products(L):
if len(L)==0:
return 0
if len(L) <= 5:
return reduce( lambda x,y:x*y, L)
pdts = ( reduce(lambda a,b:a*b,L[pos:pos+5]) for pos in range(len(L)-4)) # or pdts = map(lambda pos: reduce(lambda a,b:a*b,L[pos:pos+5],0),range(len(L)-4))
mx = reduce(lambda x,y: x if x>y else y, pdts)
return mx
pdts contains all the possible 5 tuple products, and then using reduce to mimic the max function, we find the maximum among the products.
You could do the following:
For each start index in range(0, len(L) - 5)
Map the index to the tuple of start and the product of items L[start:start + 5]
Reduce the tuples to the one that has the highest product
Get the first value of the resulting tuple = the start index of the 5 elements that have the highest product
Return the slice L[result:result + 5]
This algorithm could be further improved to avoid re-calculating sub-products, but use a "rolling product", that is updated as you reduce from left to right, dividing by the element that was dropped, and multiplying by the new element that was added.
Here is a Haskell solution, which is purely functional:
import Data.List
multiply :: [Int] -> Int
multiply = foldr (*) 1
consecutiveProducts :: [Int] -> [(Int,Int)]
consecutiveProducts xs =
[(i,multiply $ take 5 h) | (i,h) <- zipped, length h >= 5]
where
indices = reverse [0..(length xs)]
zipped = zip indices (tails xs)
myComp (i1,h1) (i2,h2) = compare h2 h1
main = print $ head $ sortBy myComp $ consecutiveProducts [4,5,3,1,5,3,2,3,5]
Here is what it does:
Starting in the last line, it computes the consecutive products from that list.
tails xs gives all the subsets starting with different starting values:
> tails [4,5,3,1,5,3,2,3,5]
[[4,5,3,1,5,3,2,3,5],[5,3,1,5,3,2,3,5],[3,1,5,3,2,3,5],[1,5,3,2,3,5],[5,3,2,3,5],[3,2,3,5],[2,3,5],[3,5],[5],[]]
From these tails we only take those that are at least 5 elements long.
Then we zip them with natural numbers such that we have the starting index associated with it.
From each of the subsets we take the first five elements.
These five elements are passed to the multiply function. There those are reduced to a single number, the product.
After that we go back to the last line, we sort the list by the product value descending.
From the resulting list we only take the first element.
And then we print the result, which is (5,450) for my input data.
This solution uses reduce to calculate a 5-value product, list comprehension for generating all of those products, tuple creation for having the index to go with each, reduce again to get the best tuple.
An if else operator is used to catch the case when there are no 5 values in the input.
from functools import reduce
def find_products(values):
return None if len(values) < 5 else reduce(
lambda best, this: this if this[1] > best[1] else best,
[(i, reduce(lambda a,b: a*b, values[i:i+5], 1)) for i in range(0, len(values)-4)]
)
result = find_products([1, 0, 8, 3, 5, 1, 0, 2, 2, 3, 2, 2, 1])
print (result)
Output for the example call is:
(7, 48)
A Pure Python Solution using recursion
First, we need to create a recursive function to find the product of a list:
def product(l, i=0, s=1):
s *= l[i]
if i+1 < len(l):
return product(l, i+1, s)
return s
which we can do some tests for:
>>> product([1, 2, 3])
6
>>> product([1, 1, 1])
3
>>> product([2, 2, 2])
8
Then, we can use this function in another recursive function to solve your problem:
def find_products(l, i=0, t=(0, -1)):
p = product(l[i:i+5])
if p > t[1]:
t = (i, p)
if i+5 < len(l):
return find_products(l, i+1, t)
return t
which works!
Here are some tests to show it working:
>>> find_products([1, 1, 5, 5, 5, 5, 5, 1, 1])
(2, 3125)
>>> find_products([1, 1, 1, 1, 1, 0, 0, 0, 0])
(0, 1)
>>> find_products([1, 4, 5, 2, 7, 9, 3, 1, 1])
(1, 2520)
want one liner using max and without max try this
from numpy import prod
l=[2,6,7,9,1,4,3]
max([prod(l[i:i+5]) for i in range(len(l))])
sorted([prod(l[i:i+5]) for i in range(len(l))])[-1] // without max
Imperative paradigm is often:
state = state0
while condition:
# change state
This is the "natural" way of programming for lot of people and you know how do that in this way.
The pure functional paradigm forbid variables, which have some advantages . It works with functions which communicates through parameters(IN) and return values(OUT). It frequently uses recursive functions.
A generic functional recursive scheme is :
f = lambda *args : result(*args) if condition(*args) else f(*newparams(*args))
Here we can find a solution with (l,i,imax,prodmax) as parameters, and:
condition = lambda l,i,_,__ : i>=len(l)-5
result = lambda _,__,*args : args
newparams = lambda l,i,imax,prodmax: (l, i+1, imax, prodmax) \
if l[i]*l[i+1]*l[i+2]*l[i+3]*l[i+4] <= prodmax \
else (l, i+1, i, l[i]*l[i+1]*l[i+2]*l[i+3]*l[i+4])
None other than functions have been defined.
You can even define no functions to do that, see here for example, but readability suffers even more.
Run :
In [1]: f([random.randint(0,9) for i in range (997)],0,0,0)
Out[1]: (386, 59049)
Python limits this approach by setting recursive depth to 2000, and from Python 3, by hiding functional tools in the module functools.

How to write a function square_odd_terms?

Write a function square_odd_terms that accepts a tuple as an argument and returns a tuple with the odd terms in the tuple squared. Even terms will remain the same.
For example, square_odd_terms((1, 2, 3, 4, 5)) returns (1, 2, 9, 4, 25) and square_odd_terms((2, 4, 6, 8, 10)) returns (2, 4, 6, 8, 10).
Answer format:
def square_odd_terms(tpl):
# Fill in your code here
I only can write a function that returns you the value of the sum of odd numbers:
def sum_odd_squares(tree):
if tree == ():
return 0
elif is_leaf(tree):
if tree%2 == 0:
return 0
else:
return tree ** 2
else:
return sum_odd_squares(tree[0])+sum_odd_squares(tree[1:])
def is_leaf(item):
return type(item) != tuple
How to do this question? How to modify the above code such that it fulfills the question requirement? What is the correct answer?
Posting Homework Questions here, huh?
Here is one way to do the problem:
Create a result tuple(empty)
Iterate through each item in your input tuple, using a loop.
Check if tupleValue[i] is odd or even.
If even, simply add it to result tuple.
If odd, compute the square and add to result tuple.
return the result tuple
Happy Coding!
You can use a list comprehension:
[ x**2 if x%2==1 else x for x in tpl ]
However, this creates a list, not a tuple. To create a tuple, you can either convert the result:
tuple([ x**2 if x%2==1 else x for x in tpl ])
Or simply use a generator expression in the first place:
tuple( x**2 if x%2==1 else x for x in tpl )

Flattening list in python

I have seen many posts regarding how to flatten a list in Python. But I was never able to understand how this is working: reduce(lambda x,y:x+y,*myList)
Could someone please explain, how this is working:
>>> myList = [[[1,2,3],[4,5],[6,7,8,9]]]
>>> reduce(lambda x,y:x+y,*myList)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
Linked already posted :
How to print list of list into one single list in python without using any for or while loop?
Flattening a shallow list in Python
Flatten (an irregular) list of lists
If anybody thinks this is duplicate to other post, I'll remove it once I understood how it works.
Thanks.
What reduce does, in plain English, is that it takes two things:
A function f that:
Accepts exactly 2 arguments
Returns a value computed using those two values
An iterable iter (e.g. a list or str)
reduce computes the result of f(iter[0],iter[1]) (the first two items of the iterable), and keeps track of this value that was just computed (call it temp). reduce then computes f(temp,iter[2]) and now keeps track of this new value. This process continues until every item in iter has been passed into f, and returns the final value computed.
The use of * in passing *myList into the reduce function is that it takes an iterable and turns it into multiple arguments. These two lines do the same thing:
myFunc(10,12)
myFunc(*[10,12])
In the case of myList, you're using a list that contains only exactly one list in it. For that reason, putting the * in front replaces myList with myList[0].
Regarding compatibility, note that the reduce function works totally fine in Python 2, but in Python 3 you'll have to do this:
import functools
functools.reduce(some_iterable)
It is equivalent to :
def my_reduce(func, seq, default=None):
it = iter(seq)
# assign either the first item from the iterable to x or the default value
# passed to my_reduce
x = next(it) if default is None else default
#For each item in iterable, update x by appying the function on x and y
for y in it:
x = func(x, y)
return x
...
>>> my_reduce(lambda a, b: a+b, *myList, default=[])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> my_reduce(lambda a, b: a+b, *myList)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> from operator import add
>>> my_reduce(add, *myList)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> my_reduce(lambda a, b: a+b, ['a', 'b', 'c', 'd'])
'abcd'
Docstring of reduce has a very good explanation:
reduce(...)
reduce(function, sequence[, initial]) -> value
Apply a function of two arguments cumulatively to the items of a sequence,
from left to right, so as to reduce the sequence to a single value.
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
((((1+2)+3)+4)+5). If initial is present, it is placed before the items
of the sequence in the calculation, and serves as a default when the
sequence is empty.
First of all, this is a very bad method. Just so you know.
reduce(f, [a, b, c, d]) runs
f(f(f(f(a, b), c), d)
Since f is lambda x,y:x+y, this is equivalent to
((a + b) + c) + d
For lists, a + b is the concatenation of the lists, so this joins each list.
This is slow because each step has to make a new list from scratch.
First, I don't know why it's wrapped in an array and then splatted (*). This will work the same way:
>>> myList = [[1,2,3],[4,5],[6,7,8,9]]
>>> reduce(lambda x,y:x+y,myList)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Explanation: reduce takes a method with two parameters - the accumulator and the element. It calls the method with each element and then sets the accumulator to the result of the lambda. Therefore, you're basically concatenating all the inner lists together.
Here's a step-by-step explanation:
accumulator is initialized to myList[0] which is [1,2,3]
lambda is called with [1,2,3] and [4,5], it returns [1,2,3,4,5], which is assigned to the accumulator
lambda is called with [1,2,3,4,5] and [6,7,8,9], it returns [1,2,3,4,5,6,7,8,9]
no more elements left, so reduce returns that

How to find elements existing in two lists but with different indexes

I have two lists of the same length which contains a variety of different elements. I'm trying to compare them to find the number of elements which exist in both lists, but have different indexes.
Here are some example inputs/outputs to demonstrate what I mean:
>>> compare([1, 2, 3, 4], [4, 3, 2, 1])
4
>>> compare([1, 2, 3], [1, 2, 3])
0
# Each item in the first list has the same index in the other
>>> compare([1, 2, 4, 4], [1, 4, 4, 2])
2
# The 3rd '4' in both lists don't count, since they have the same indexes
>>> compare([1, 2, 3, 3], [5, 3, 5, 5])
1
# Duplicates don't count
The lists are always the same size.
This is the algorithm I have so far:
def compare(list1, list2):
# Eliminate any direct matches
list1 = [a for (a, b) in zip(list1, list2) if a != b]
list2 = [b for (a, b) in zip(list1, list2) if a != b]
out = 0
for possible in list1:
if possible in list2:
index = list2.index(possible)
del list2[index]
out += 1
return out
Is there a more concise and eloquent way to do the same thing?
This python function does hold for the examples you provided:
def compare(list1, list2):
D = {e:i for i, e in enumerate(list1)}
return len(set(e for i, e in enumerate(list2) if D.get(e) not in (None, i)))
since duplicates don't count, you can use sets to find only the elements in each list. A set only holds unique elements. Then select only the elements shared between both using list.index
def compare(l1, l2):
s1, s2 = set(l1), set(l2)
shared = s1 & s2 # intersection, only the elements in both
return len([e for e in shared if l1.index(e) != l2.index(e)])
You can actually bring this down to a one-liner if you want
def compare(l1, l2):
return len([e for e in set(l1) & set(l2) if l1.index(e) != l2.index(e)])
Alternative:
Functionally you can use the reduce builtin (in python3, you have to do from functools import reduce first). This avoids construction of the list which saves excess memory usage. It uses a lambda function to do the work.
def compare(l1, l2):
return reduce(lambda acc, e: acc + int(l1.index(e) != l2.index(e)),
set(l1) & set(l2), 0)
A brief explanation:
reduce is a functional programming contruct that reduces an iterable to a single item traditionally. Here we use reduce to reduce the set intersection to a single value.
lambda functions are anonymous functions. Saying lambda x, y: x + 1 is like saying def func(x, y): return x + y except that the function has no name. reduce takes a function as its first argument. The first argument a the lambda receives when used with reduce is the result of the previous function, the accumulator.
set(l1) & set(l2) is a set consisting of unique elements that are in both l1 and l2. It is iterated over, and each element is taken out one at a time and used as the second argument to the lambda function.
0 is the initial value for the accumulator. We use this since we assume there are 0 shared elements with different indices to start.
I dont claim it is the simplest answer, but it is a one-liner.
import numpy as np
import itertools
l1 = [1, 2, 3, 4]
l2 = [1, 3, 2, 4]
print len(np.unique(list(itertools.chain.from_iterable([[a,b] for a,b in zip(l1,l2) if a!= b]))))
I explain:
[[a,b] for a,b in zip(l1,l2) if a!= b]
is the list of couples from zip(l1,l2) with different items. Number of elements in this list is number of positions where items at same position differ between the two lists.
Then, list(itertools.chain.from_iterable() is for merging component lists of a list. For instance :
>>> list(itertools.chain.from_iterable([[3,2,5],[5,6],[7,5,3,1]]))
[3, 2, 5, 5, 6, 7, 5, 3, 1]
Then, discard duplicates with np.unique(), and take len().

Categories