I have two lists
a = [1,2,3]
b = [9,10]
I want to combine (zip) these two lists into one list c such that
c = [(1,9), (2,10), (3, )]
Is there any function in standard library in Python to do this?
Normally, you use itertools.zip_longest for this:
>>> import itertools
>>> a = [1, 2, 3]
>>> b = [9, 10]
>>> for i in itertools.zip_longest(a, b): print(i)
...
(1, 9)
(2, 10)
(3, None)
But zip_longest pads the shorter iterable with Nones (or whatever value you pass as the fillvalue= parameter). If that's not what you want then you can use a comprehension to filter out the Nones:
>>> for i in (tuple(p for p in pair if p is not None)
... for pair in itertools.zip_longest(a, b)):
... print(i)
...
(1, 9)
(2, 10)
(3,)
but note that if either of the iterables has None values, this will filter them out too. If you don't want that, define your own object for fillvalue= and filter that instead of None:
sentinel = object()
def zip_longest_no_fill(a, b):
for i in itertools.zip_longest(a, b, fillvalue=sentinel):
yield tuple(x for x in i if x is not sentinel)
list(zip_longest_no_fill(a, b)) # [(1, 9), (2, 10), (3,)]
Another way is map:
a = [1, 2, 3]
b = [9, 10]
c = map(None, a, b)
Although that will too contain (3, None) instead of (3,). To do that, here's a fun line:
c = (tuple(y for y in x if y is not None) for x in map(None, a, b))
It's not too hard to just write the explicit Python to do the desired operation:
def izip_short(a, b):
ia = iter(a)
ib = iter(b)
for x in ia:
try:
y = next(ib)
yield (x, y)
except StopIteration:
yield (x,)
break
for x in ia:
yield (x,)
for y in ib:
yield (None, y)
a = [1, 2, 3]
b = [9, 10]
list(izip_short(a, b))
list(izip_short(b, a))
I wasn't sure how you would want to handle the b sequence being longer than the a sequence, so I just stuff in a None for the first value in the tuple in that case.
Get an explicit iterator for each sequence. Run the a iterator as a for loop, while manually using next(ib) to get the next value from the b sequence. If we get a StopIteration on the b sequence, we break the loop and then for x in ia: gets the rest of the a sequence; after that for y in ib: will do nothing because that iterator is already exhausted. Alternatively, if the first for x in ia: loop exhausts the a iterator, the second for x in ia: does nothing but there could be values left in the b sequence and the for y in ib: loop collects them.
Single line:
c = zip(a, b) + [(x,) for x in a[len(b):]] + [(x,) for x in b[len(a):]]
If you want to reuse this:
def mergeUsNicely(a, b):
def tupleMe(val):
return (val,)
return zip(a, b) + map(tupleMe, a[len(b):]) + map(tupleMe, b[len(a):])
This answer is an extension of the top answer that allows for arbitrary inputs instead of only two.
import itertools
sentinel = object()
def zip_longest_no_fill(*args):
for i in itertools.zip_longest(*args, fillvalue=sentinel):
yield tuple(x for x in i if x is not sentinel)
Related
I have this function:
import concurrent.futures
def function_abc(a,b):
#do something
I call this function with this:
with concurrent.futures.ThreadPoolExecutor() as executor:
executor.map(function_abc, a, b)
But this does not work.
If I remove the argument "b":
with concurrent.futures.ThreadPoolExecutor() as executor:
executor.map(function_abc, a)
It works anyway. But I don't get the additional argument inside of this function.
What am I doing wrong?
Pass your arguments as separate iterables, one for a and another for b:
In [2]: with concurrent.futures.ThreadPoolExecutor() as ex:
...: m = ex.map(pow, (2, 2, 2, 2), (2, 3, 4, 5))
...:
...: for item in m:
...: print(item)
...:
4
8
16
32
To be more explicit:
ex.map(func_of_a_and_b, (a1, a2, a3, a4, ...), (b1, b2, b3, b4, ...))
You can pass in a list of tuples instead. Tuples are defined as (a, b, c...) and cannot be modified, but you can access them the same as lists (with an index, reverse index, slicing).
So basically nested lists but python somelist = [sometuple, sometuple, sometuple]
For example:
## Lets say this function multiplies a and b
# This is a compact and fast way
def function_abc(tup):
return tup[0]*tup[1]
## This makes it possible to modify the variables (not the tuple) and a bit nicer for more complicated code
## This also has lists
def function_abc(tup):
a = tup[0]
b = tup[1]
a = a/2
#tup[0] = tup[0]/2 <- Will show error as tuples are immutable
return a*b
def combine(a, b):
if len(a) != len(b):
print("Wrong lengths!")
l = []
for i in range(0, len(a)):
l.append((a[i], b[i]))
return l
## In map:
a = [1, 2, 3]
b = [10, 9, 8]
results = list(map(function_abc, combine(a, b))) # Should work
print(results)
Note that you will need a combine function to combine the 2 lists into a list of tuples
EDIT: Fixed code with combine function. What was I thinking?
I have two lists
a = [1,2,3]
b = [9,10]
I want to combine (zip) these two lists into one list c such that
c = [(1,9), (2,10), (3, )]
Is there any function in standard library in Python to do this?
Normally, you use itertools.zip_longest for this:
>>> import itertools
>>> a = [1, 2, 3]
>>> b = [9, 10]
>>> for i in itertools.zip_longest(a, b): print(i)
...
(1, 9)
(2, 10)
(3, None)
But zip_longest pads the shorter iterable with Nones (or whatever value you pass as the fillvalue= parameter). If that's not what you want then you can use a comprehension to filter out the Nones:
>>> for i in (tuple(p for p in pair if p is not None)
... for pair in itertools.zip_longest(a, b)):
... print(i)
...
(1, 9)
(2, 10)
(3,)
but note that if either of the iterables has None values, this will filter them out too. If you don't want that, define your own object for fillvalue= and filter that instead of None:
sentinel = object()
def zip_longest_no_fill(a, b):
for i in itertools.zip_longest(a, b, fillvalue=sentinel):
yield tuple(x for x in i if x is not sentinel)
list(zip_longest_no_fill(a, b)) # [(1, 9), (2, 10), (3,)]
Another way is map:
a = [1, 2, 3]
b = [9, 10]
c = map(None, a, b)
Although that will too contain (3, None) instead of (3,). To do that, here's a fun line:
c = (tuple(y for y in x if y is not None) for x in map(None, a, b))
It's not too hard to just write the explicit Python to do the desired operation:
def izip_short(a, b):
ia = iter(a)
ib = iter(b)
for x in ia:
try:
y = next(ib)
yield (x, y)
except StopIteration:
yield (x,)
break
for x in ia:
yield (x,)
for y in ib:
yield (None, y)
a = [1, 2, 3]
b = [9, 10]
list(izip_short(a, b))
list(izip_short(b, a))
I wasn't sure how you would want to handle the b sequence being longer than the a sequence, so I just stuff in a None for the first value in the tuple in that case.
Get an explicit iterator for each sequence. Run the a iterator as a for loop, while manually using next(ib) to get the next value from the b sequence. If we get a StopIteration on the b sequence, we break the loop and then for x in ia: gets the rest of the a sequence; after that for y in ib: will do nothing because that iterator is already exhausted. Alternatively, if the first for x in ia: loop exhausts the a iterator, the second for x in ia: does nothing but there could be values left in the b sequence and the for y in ib: loop collects them.
Single line:
c = zip(a, b) + [(x,) for x in a[len(b):]] + [(x,) for x in b[len(a):]]
If you want to reuse this:
def mergeUsNicely(a, b):
def tupleMe(val):
return (val,)
return zip(a, b) + map(tupleMe, a[len(b):]) + map(tupleMe, b[len(a):])
This answer is an extension of the top answer that allows for arbitrary inputs instead of only two.
import itertools
sentinel = object()
def zip_longest_no_fill(*args):
for i in itertools.zip_longest(*args, fillvalue=sentinel):
yield tuple(x for x in i if x is not sentinel)
I'm trying to write a function for adding 2D vectors.
I'm trying to combine the map() function, getting a list using the zip() function (which will zip 2 tuples).
This is the code:
a = (1, 2)
b = (3, 4)
c = list(map(lambda x, y: x+y, list(zip(a, b))))
print(c)
So the way I see it, zip(a, b) returns a zip object containing the following tuples: (1, 3), (2, 4). It is then converted to a list. I'm getting the following error:
TypeError: () missing 1 required positional argument: 'y'
So my guess is that the lambda function is not taking the second number in each tuple.
Is there any way to do this?
Only one parameter for lambda like:
c = list(map(lambda x: sum(x), zip(a, b)))
But once we are using sum, we can map it directly like:
c = list(map(sum, zip(a, b)))
Test Code:
a = (1, 2)
b = (3, 4)
c = list(map(sum, zip(a, b)))
print(c)
Result:
[4, 6]
In Python2, you can use specific unpacking syntax in a lambda:
a = (1, 2)
b = (3, 4)
c = list(map(lambda (x, y): x+y, list(zip(a, b))))
However, in Python3, you will have to use sum:
c = list(map(lambda x_y: sum(x_y), list(zip(a, b))))
zip returns a tuples, so you could sum like this example:
list(map(lambda x: x[0] + x[1], zip(a, b)))
output:
[4, 6]
The lambda receives one parameter, which is a 2-tuple returned by zip.
You can access it element-wise: lambda pair: pair[0] + pair[1].
You can apply sum() to it.
In Python 2, you can unpack it right in the signature: lambda (x, y): x + y.
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)
I'm trying to compare two lists to determine if one is a rotation (cyclic permutation) of the other, e.g.:
a = [1, 2, 3]
b = [1, 2, 3] or [2, 3, 1] or [3, 1, 2]
are all matches, whereas:
b = [3, 2, 1] is not
To do this I've got the following code:
def _matching_lists(a, b):
return not [i for i, j in zip(a,b) if i != j]
def _compare_rotated_lists(a, b):
rotations = [b[i:] + b[:i] for i in range(len(b))]
matches = [i for i in range(len(rotations)) if _matching_lists(a, rotations[i])]
return matches
This builds a list of all possible rotations of b and then compares each one. Is it possible to do this without building the intermediate list? Performance isn't important since the lists will typically only be four items long. My primary concern is clarity of code.
The lists will always have the same length.
Best answer (keeping the list of matching rotations) seems to be:
def _compare_rotated_lists(a, b):
return [i for i in range(len(b)) if a == b[i:] + b[:i]]
You don't need the function _matching_lists, as you can just use ==:
>>> [1,2,3] == [1,2,3]
True
>>> [1,2,3] == [3,1,2]
False
I suggest using any() to return as soon a match is found, and using a generator expression to avoid constructing a list of rotations in memory:
def _compare_rotated_lists(a, b):
"""Return `True` if the list `a` is equal to a rotation of the list `b`."""
return any(a == b[i:] + b[:i] for i in range(len(b)))
You might consider checking that the lists are the same length, to reject the easy case quickly.
return len(a) == len(b) and any(a == b[i:] + b[:i] for i in range(len(b)))
As discussed in comments, if you know that the elements of a and b are hashable, you can do the initial comparison using collections.Counter:
return Counter(a) == Counter(b) and any(a == b[i:] + b[:i] for i in range(len(b)))
and if you know that the elements of a and b are comparable, you can do the initial comparison using sorted:
return sorted(a) == sorted(b) and any(a == b[i:] + b[:i] for i in range(len(b)))
If I understood correctly, you want to find if b is a permutation of a, but not a reversed? There's a very simple, readable, and general solution:
>>> from itertools import permutations
>>> a = (1, 2, 3)
>>> b = (3, 1, 2)
>>> c = (3, 2, 1)
>>> results = set(permutations(a)) - set((a, tuple(sorted(a, reverse=True))))
>>> b in results
True
>>> c in results
False
How about:
def canon(seq):
n = seq.index(min(seq))
return seq[n:] + seq[:n]
def is_rotation(a, b):
return canon(a) == canon(b)
print is_rotation('abcd', 'cdab') # True
print is_rotation('abcd', 'cdba') # False
No need to generate all rotations just to find out if two lists are rotation of each other.
I tested this code with a few examples, and it worked well.
def compare(a,b):
firstInA = a[0]
firstInB = b.index(firstInA)
secondInA = a[1]
secondInB = b.index(secondInA)
if (secondInB == firstInB + 1) or (secondInB == 0 and firstInB == 2):
return True
else:
return False
I tried:
a = [1,2,3]
b = [1,2,3]
print(compare(a,b))
c = [1,2,3]
d = [3,1,2]
print(compare(c,d))
e = [1,2,3]
f = [3,2,1]
print(compare(e,f))
They returned True,True,False
This only works with lists of size 3. If you want more, within the if statement, add a thirdInA and thirdInB, and you will always need to have one less than the length of the list, because if you know all but one is in place, then there is only on spot left for the last to be.