jomurmiranda
jomurmiranda

Reputation: 113

List comprehension to create a list

Can the following be coded more compactly using list comprehension?

labels=[]
for i in range(10):
    labels=labels+[i]+9*['']
labels=labels+[10]

Upvotes: 2

Views: 151

Answers (4)

chris
chris

Reputation: 2063

This reads to me like "create an array 101 elements in length, where each item is an empty string if its index is a non-integer multiple of 10, otherwise it's the index-divided-by-10". As a list comprehension:

>>> ['' if i % 10 else i / 10 for i in xrange(101)]
[0, '', '', '', '', '', '', '', '', '', 1, '', '', '', '', '', '', '', '', '', 2, '', '', '', '', '', '', '', '', '', 3, '', '', '', '', '', '', '', '', '', 4, '', '', '', '', '', '', '', '', '', 5, '', '', '', '', '', '', '', '', '', 6, '', '', '', '', '', '', '', '', '', 7, '', '', '', '', '', '', '', '', '', 8, '', '', '', '', '', '', '', '', '', 9, '', '', '', '', '', '', '', '', '', 10]

That said, if the list comprehension isn't an absolute requirement, I find something like this quite a bit easier to grok:

>>> y = []
>>> for i in xrange(10):
...     y.append(i)
...     y.extend([''] * 9)
...
>>> y.append(10)

(which is almost identical to your original approach)

Upvotes: 0

Abhinav Mishra
Abhinav Mishra

Reputation: 20

Here is the code you are looking for

labels=[j for i in range(10) for j in [i]+9*['']]+[10]

Upvotes: 0

Alex Reynolds
Alex Reynolds

Reputation: 96984

It is convoluted, but if you want a list comprehension, this is one way to do it:

[i for s in [[x] + 9*[''] if x < 10 else [x] for x in range(11)] for i in s]

Demo:

>>> labels = [i for s in [[x] + 9*[''] if x < 10 else [x] for x in range(11)] for i in s]
>>> labels
[0, '', '', '', '', '', '', '', '', '', 1, '', '', '', '', '', '', '', '', '', 2, '', '', '', '', '', '', '', '', '', 3, '', '', '', '', '', '', '', '', '', 4, '', '', '', '', '', '', '', '', '', 5, '', '', '', '', '', '', '', '', '', 6, '', '', '', '', '', '', '', '', '', 7, '', '', '', '', '', '', '', '', '', 8, '', '', '', '', '', '', '', '', '', 9, '', '', '', '', '', '', '', '', '', 10]

By way of comparison:

>>> labels=[]
>>> for i in range(10):
...     labels=labels+[i]+9*['']
... 
>>> labels=labels+[10]
>>> labels
[0, '', '', '', '', '', '', '', '', '', 1, '', '', '', '', '', '', '', '', '', 2, '', '', '', '', '', '', '', '', '', 3, '', '', '', '', '', '', '', '', '', 4, '', '', '', '', '', '', '', '', '', 5, '', '', '', '', '', '', '', '', '', 6, '', '', '', '', '', '', '', '', '', 7, '', '', '', '', '', '', '', '', '', 8, '', '', '', '', '', '', '', '', '', 9, '', '', '', '', '', '', '', '', '', 10]

Upvotes: 1

David Maze
David Maze

Reputation: 159875

Here's one way to break this down.

If you look at the main body of your loop, you can restructure it as generating a sublist for each number, and then combining the sublists together.

sublists = []
for i in range(10):
  sublists.append([i] + 9 * [''])

labels = []
for sublist in sublists:
  labels = labels + sublist

labels = labels + [10]

The first part of this takes a list of numbers, calls the same function on each of them, and produces a list of results. This operation is map (and indeed has this name in many languages). The second part takes a list of lists and flattens them into one big list; many languages have a "concat" or "flatten" operation, but in Python it can be a little clunky.

from itertools import chain
sublists = map(range(10), lambda i: [i] + 9 * [''])
labels = list(chain.from_iterable(sublists))
labels = labels + [10]

The map() call in particular easily transforms into a list comprehension (or a generator comprehension)

from itertools import chain
sublists = [[i] + 9 * [''] for i in range(10)]
labels = list(chain.from_iterable(sublists))
labels = labels + [10]

and so if you want to turn this into a one-liner you can have

from itertools import chain
labels = list(chain.from_iterable([i] + 9 * [''] for i in range(10))) + [10]

For something completely different, you could potentially use a generator function to make it clearer what you're doing. Really, for each item in the input, you're emitting the item, and if it's not the last item, emitting nine lists containing empty strings. You can then take the sequence produced by the generator function and convert it to a list.

def emit_with_blanks(iter):
  l = list(iter)
  for i, n in enumerate(l):
    yield [i]
    if i == len(l) - 1:
      break
    for _ in range(9):
      yield ['']

labels = list(emit_with_blanks(range(10))

This is definitely longer and slower (in a way that shouldn't matter in practice) but it could be easier to reason about what it's doing than the one-liner, especially if you come back to it six months later and are trying to remember what exactly the code does.

Upvotes: 2

Related Questions