'zip list with a single element

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?



Solution 1:[1]

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')]

Solution 2:[2]

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')]

Solution 3:[3]

lst = [1,2,3,4]
tups = [(itm, 'a') for itm in lst]
tups

> [(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]

Solution 4:[4]

>>> l = [1, 2, 3, 4]
>>> list(zip(l, "a"*len(l)))
[(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]

Solution 5:[5]

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.

Solution 6:[6]

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:

  • Empty iterables are correctly handled.
  • There can be multiple iterable and/or scalar arguments.
  • The order of the scalar/iterable arguments doesn't need to be known.
  • If there are multiple iterable arguments, you can check that they are the same length with strict=True.
  • You can easily control whether or not strings should be treated as iterables (by default they are not).

Solution 7:[7]

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')]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ahmed
Solution 2 styvane
Solution 3 kztd
Solution 4 rustam s
Solution 5 ahmed
Solution 6 Kale Kundert
Solution 7