zegkljan
zegkljan

Reputation: 8401

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?

Upvotes: 59

Views: 45702

Answers (9)

Gergely M
Gergely M

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

Padraic Cunningham
Padraic Cunningham

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

Kale Kundert
Kale Kundert

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:

  • 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).

Upvotes: 1

rustam s
rustam s

Reputation: 163

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

Upvotes: 7

Gokhan Gunay
Gokhan Gunay

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

kztd
kztd

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

Sede
Sede

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

ahmed
ahmed

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

kirbyfan64sos
kirbyfan64sos

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

Related Questions