Most Pythonique, Efficient, Compact, Elegant Way to Do This ... ?
Given a list of strings, how would you iterpolate a multi-character string in front of each element?
For example, given:
>>> k = ['the quick', 'brown fox', 'jumps over', 'the lazy', 'dog']
['-c', 'the quick', '-c', 'brown fox', '-c', 'jumps over', '-c', 'the lazy', '-c', 'dog']
Of course, the naive solution would be to compose a new list by iterate over the original list:
>>> result = [] >>> for i in k: ... result.append('-c') ... result.append(i)
Alternatively, you could take advantage of the itertools module:
>>> import itertools >>> result = list(itertools.chain.from_iterable(itertools.product(['-c'], k)))
This second form is certainly more compact, and probably more efficient ... but perhaps lacks some of the clarity and elegance one might expect from Python.
Is there another solution?
feed
Comments
29 comments postedGo with the one that is easiest to read. That would be the so-called "naive solution" as it is perfectly clear what is going on and uses very common python idioms. Many of the other methods require more time to study in order to understand their purpose and function. So you are obfuscating the code and making it more difficult to read and debug for .. what? To be clever? I say that, unless you can demonstrate a clear and significant speed advantage to any of the other methods, use the one that optimizes for easy reading and programmer time.
... but, as the dozens of approaches suggested here demonstrate, sometimes that way may not be so clear or objectively evident.
So far, IMHO, I think that the most concise approach of the many excellent ones proposed is the one using
sum, as suggested by number of you:However, for conciseness and clarity, I think the approach using
extend, also as suggested by a number of you, seems to shine:I guess these two solutions in some respects epitomize two of the major programming paradigms supported by Python, and which comes across as more Pythonic depends on which programming paradigm we are currently most comfortable with.
['-c'] + ' -c '.join(k).split()
>>> k = ['the quick', 'brown fox', 'jumps over', 'the lazy', 'dog']
>>> ['-c' if i % 2 == 0 else k[(i - 1) / 2] for i in xrange(2 * len(k))]
['-c', 'the quick', '-c', 'brown fox', '-c', 'jumps over', '-c', 'the lazy', '-c', 'dog']
>>> reduce(lambda a, b: a + ['-c', b], k, [])
['-c', 'the quick', '-c', 'brown fox', '-c', 'jumps over', '-c', 'the lazy', '-c', 'dog']
>>> intersperse = lambda a, l: [] if not l else ([a] + l if len(l) == 1 else [a, l[0]] + intersperse(a, l[1:]))
>>> intersperse('-c', k)
['-c', 'the quick', '-c', 'brown fox', '-c', 'jumps over', '-c', 'the lazy', '-c', 'dog']
>>> result = 2*len(k)*["-c"]
>>> result[1::2] = k
>>> result
['-c', 'the quick', '-c', 'brown fox', '-c', 'jumps over', '-c', 'the lazy', '-c', 'dog']
... is to start by spelling it "pythonic"
:-)
For the record, the "naive solution" is very nearly the only one of these that I can read without a pencil and paper to work out the result. I vote naive. :-)
Another version of join() and split()
>>> '*'.join([ '-c*'+i for i in k]).split('*')
['-c', 'the quick', '-c', 'brown fox', '-c', 'jumps over', '-c', 'the lazy', '-c', 'dog']
I like itertools and extend versions.
orig = ['foo', 'bar', 'baz'] for i in xrange(len(orig)): orig.insert(i*2, '-c')So how does this work? It appears to be using [] as the "start" value, which doesn't make any sense to me.
(I came up with the extend solution myself.. as x = []; map(x.extend, [('-c', z) for z in k]))
def f(iterable): for i in iterable: yield '-c' yield ior, more generically, use a weave functiondef weave(*iterables): iters = map(iter, iterables) while 1: for it in iters: yield it.next() list(repeat('-c'), k)weave should be added to itertools IMOThe following also work:
list(itertools.chain(*zip(itertools.repeat('-c'), k)))
list(itertools.chain.from_iterable(zip(itertools.repeat('-c'), k)))
Here is another one-liner:
result = [x for t in [('-c',i) for i in k] for x in t]
Not sure if it's pythonic, but this one-liner will do the trick:
result = sum([['-c',i] for i in k], [])
>>> ('-c|'+'|-c|'.join(k)).split('|')
['-c', 'the quick', '-c', 'brown fox', '-c', 'jumps over', '-c', 'the lazy', '-c', 'dog']
>>> result=[]
>>> [result.extend(('-c', x)) for x in k]
[None, None, None, None, None]
>>> result
['-c', 'the quick', '-c', 'brown fox', '-c', 'jumps over', '-c', 'the lazy', '-c', 'dog']
Hi,
What about:
result = sum([[e, '-c'] for e in k], [])
If we timeit (from timeit import timeit) your two solutions as f1 and f2, and mine as f3, your first solution seems the fastest:
f1: 4.2329611778259277
f2: 8.0269858837127686
f3: 5.8209688663482666
Virtuo
list(flatten(itertools.izip_longest([],k,fillvalue='-c')) )
list(flatten(itertools.izip_longest([],k,fillvalue='-c')) )I'd probably use a flattener, although it's not likely to be as fast-
from itertools import izip, repeat
from snakeoil.lists import flatten
result = flatten(izip(repeat('-c'), k))
# snakeoil module being http://pkgcore.org/trac/pkgcore/browser/snakeoil/snakeoil/lists.py
Always kind of wished stdlib had something similar to the iflatten/iflatten_instance functions in there...
using iPython shell
ipython
In [2]: a=['one', 'two', 'three']
In [3]: b = ['-c'] * len(a)
In [4]: b
Out[4]: ['-c', '-c', '-c']
In [5]: l = []
In [6]: [l.extend(i) for i in zip(b, a)]
Out[6]: [None, None, None]
In [7]: l
Out[7]: ['-c', 'one', '-c', 'two', '-c', 'three']
I think that using extend method in the first case will be more clear
for i in k;
result.extend(('-c',i))
and you an also do it as a comprehension, if you like...
result = []
[result.extend(('-c', i)) for i in k]
[j for i in k for j in ['-c', i]]list(itertools.chain(*zip(itertools.repeat("-c"), k)))list(sum(zip(['-c'] * len(k), k), ()))If it's okay to change k inserting is another possibility:
for i in range(len(k)-1,-1,-1):k.insert(i, "-c")
Sebastian
Post new comment