zip list with a single element

Question:

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?

Asked By: zegkljan

||

Answers:

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)
Answered By: kirbyfan64sos

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.

Answered By: Padraic Cunningham

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')]
Answered By: ahmed

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')]
Answered By: styvane
lst = [1,2,3,4]
tups = [(itm, 'a') for itm in lst]
tups

> [(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
Answered By: kztd

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')]
Answered By: Gokhan Gunay
>>> l = [1, 2, 3, 4]
>>> list(zip(l, "a"*len(l)))
[(1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')]
Answered By: rustam s

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).
Answered By: Kale Kundert

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.

Answered By: Gergely M
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.