python: most elegant way to intersperse a list with an element

Input:

intersperse(666, ["once", "upon", "a", 90, None, "time"])

Output:

["once", 666, "upon", 666, "a", 666, 90, 666, None, 666, "time"]

What’s the most elegant (read: Pythonic) way to write intersperse?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

I would have written a generator myself, but like this:

def joinit(iterable, delimiter):
    it = iter(iterable)
    yield next(it)
    for x in it:
        yield delimiter
        yield x

Method 2

itertools to the rescue
– or –
How many itertools functions can you use in one line?

from itertools import chain, izip, repeat, islice

def intersperse(delimiter, seq):
    return islice(chain.from_iterable(izip(repeat(delimiter), seq)), 1, None)

Usage:

>>> list(intersperse(666, ["once", "upon", "a", 90, None, "time"])
["once", 666, "upon", 666, "a", 666, 90, 666, None, 666, "time"]

Method 3

Another option that works for sequences:

def intersperse(seq, value):
    res = [value] * (2 * len(seq) - 1)
    res[::2] = seq
    return res

Method 4

Solution is trivial using more_itertools.intersperse:

>>> from more_itertools import intersperse
>>> list(intersperse(666, ["once", "upon", "a", 90, None, "time"]))
['once', 666, 'upon', 666, 'a', 666, 90, 666, None, 666, 'time']

Technically, this answer isn’t “writing” intersperse, it’s just using it from another library. But it might save others from having to reinvent the wheel.

Method 5

I would go with a simple generator.

def intersperse(val, sequence):
    first = True
    for item in sequence:
        if not first:
            yield val
        yield item
        first = False

and then you can get your list like so:

>>> list(intersperse(666, ["once", "upon", "a", 90, None, "time"]))
['once', 666, 'upon', 666, 'a', 666, 90, 666, None, 666, 'time']

alternatively you could do:

def intersperse(val, sequence):
    for i, item in enumerate(sequence):
        if i != 0:
            yield val
        yield item

I’m not sure which is more pythonic

Method 6

def intersperse(word,your_list):
    x = [j for i in your_list for j in [i,word]]

>>> intersperse(666, ["once", "upon", "a", 90, None, "time"])
['once', 666, 'upon', 666, 'a', 666, 90, 666, None, 666, 'time', 666]

[Edit] Corrected code below:

def intersperse(word,your_list):
    x = [j for i in your_list for j in [i,word]]
    x.pop()
    return x

>>> intersperse(666, ["once", "upon", "a", 90, None, "time"])
['once', 666, 'upon', 666, 'a', 666, 90, 666, None, 666, 'time']

Method 7

How about:

from itertools import chain,izip_longest

def intersperse(x,y):
     return list(chain(*izip_longest(x,[],fillvalue=y)))

Method 8

The basic and easy you could do is:

a = ['abc','def','ghi','jkl']

# my separator is : || separator ||
# hack is extra thing : --

'--|| separator ||--'.join(a).split('--')

output:

['abc','|| separator ||','def','|| separator ||','ghi','|| separator ||','jkl']

Method 9

Dunno if it’s pythonic, but it’s pretty simple:

def intersperse(elem, list):
    result = []
    for e in list:
      result.extend([e, elem])
    return result[:-1]

Method 10

I just came up with this now, googled to see if there was something better… and IMHO there wasn’t 🙂

def intersperse(e, l):    
    return list(itertools.chain(*[(i, e) for i in l]))[0:-1]

Method 11

I believe this one looks pretty nice and easy to grasp compared to the yield next(iterator) or itertools.iterator_magic() one 🙂

def list_join_seq(seq, sep):
  for i, elem in enumerate(seq):
    if i > 0: yield sep
    yield elem

print(list(list_join_seq([1, 2, 3], 0)))  # [1, 0, 2, 0, 3]

Method 12

This works:

>>> def intersperse(e, l):
...    return reduce(lambda x,y: x+y, zip(l, [e]*len(l)))
>>> intersperse(666, ["once", "upon", "a", 90, None, "time"])
('once', 666, 'upon', 666, 'a', 666, 90, 666, None, 666, 'time', 666)

If you don’t want a trailing 666, then return reduce(...)[:-1].

Method 13

Seems general and efficient:

def intersperse(lst, fill=...):
    """
    >>> list(intersperse([1,2,3,4]))
    [1, Ellipsis, 2, Ellipsis, 3, Ellipsis, 4]
    """
    return chain(*zip(lst[:-1], repeat(fill)), [lst[-1]])

Method 14

You can use Python’s list comprehension:

def intersperse(iterable, element):
    return [iterable[i // 2] if i % 2 == 0 else element for i in range(2 * len(iterable) - 1)]

Method 15

def intersperse(items, delim):
    i = iter(items)
    return reduce(lambda x, y: x + [delim, y], i, [i.next()])

Should work for lists or generators.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x