Reputation: 8401
I have a list of some elements, e.g. [1, 2, 3, 4]
and a single object, e.g. 'a'
. I want to produce a list of tuples with the elements of the list in the first position and the single object in the second position: [(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
.
I could do it with zip
like this:
def zip_with_scalar(l, o): # l - the list; o - the object
return list(zip(l, [o] * len(l)))
However, this gives me a feeling of creating and unnecessary list of repeating element.
Another possibility is
def zip_with_scalar(l, o):
return [(i, o) for i in l]
which is very clean and pythonic indeed, but here I do the whole thing "manually". In Haskell I would do something like
zipWithScalar l o = zip l $ repeat o
Is there any built-in function or trick, either for the zipping with scalar or for something that would enable me to use ordinary zip, i.e. sort-of infinite list?
Upvotes: 59
Views: 45702
Reputation: 733
I'm surprised no one came up with a simple Pythonic solution using list comprehension or a generator object.
Take that we have a list of strings -> data And we want to use zip() by adding a single integer 2001 (year) to each item. We can create a list that has the same length as the data list and contains the integer 2001 in each item.
data = ['RIN1', 'RIN2', 'RIN3', 'RIN4', 'RIN5', 'RIN6', 'RIN7']
zipped = list(zip(data, [2001 for _ in data]))
But there's an even better solution using a generator - thus we can avoid creating a potentially huge list of the same value.
data = ['RIN1', 'RIN2', 'RIN3', 'RIN4', 'RIN5', 'RIN6', 'RIN7']
zipped = list(zip(data, (2001 for _ in data)))
So instead of creating the actual list with [2001 for _ in data]
we can define a generator object: (2001 for _ in data)
The difference (on the surface) is only the type of brackets: [...] -> (...)
I hope some of you find this useful.
Upvotes: 3
Reputation: 180401
You could also use zip_longest with a fillvalue
of o
:
from itertools import zip_longest
def zip_with_scalar(l, o): # l - the list; o - the object
return zip_longest(l, [o], fillvalue=o)
print(list(zip_with_scalar([1, 2, 3, 4] ,"a")))
Just be aware that any mutable values used for o
won't be copied whether using zip_longest or repeat.
Upvotes: 6
Reputation: 1494
The more-itertools
library recently added a zip_broadcast()
function that solves this problem well:
>>> from more_itertools import zip_broadcast
>>> list(zip_broadcast([1,2,3,4], 'a'))
[(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
This is a much more general solution than the other answers posted here:
strict=True
.Upvotes: 1
Reputation: 163
>>> l = [1, 2, 3, 4]
>>> list(zip(l, "a"*len(l)))
[(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
Upvotes: 7
Reputation: 9
Just define a class with infinite iterator which is initialized with the single element you want to injected in the lists:
class zipIterator:
def __init__(self, val):
self.__val = val
def __iter__(self):
return self
def __next__(self):
return self.__val
and then create your new list from this class and the lists you have:
elements = [1, 2, 3, 4]
key = 'a'
res = [it for it in zip(elements, zipIterator(key))]
the result would be:
>>res
[(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
Upvotes: 0
Reputation: 3415
lst = [1,2,3,4]
tups = [(itm, 'a') for itm in lst]
tups
> [(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
Upvotes: 8
Reputation: 61225
This is a perfect job for the itertools.cycle
class.
from itertools import cycle
def zip_with_scalar(l, o):
return zip(i, cycle(o))
Demo:
>>> from itertools import cycle
>>> l = [1, 2, 3, 4]
>>> list(zip(l, cycle('a')))
[(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
Upvotes: 11
Reputation: 5600
You can use the built-in map
function:
>>> elements = [1, 2, 3, 4]
>>> key = 'a'
>>> map(lambda e: (e, key), elements)
[(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
Upvotes: 24
Reputation: 10727
This is the cloest to your Haskell solution:
import itertools
def zip_with_scalar(l, o):
return zip(l, itertools.repeat(o))
You could also use generators, which avoid creating a list like comprehensions do:
def zip_with_scalar(l, o):
return ((i, o) for i in l)
Upvotes: 77