41

I need to incrementally fill a list or a tuple of lists. Something that looks like this:

result = []
firstTime = True
for i in range(x):
    for j in someListOfElements:
        if firstTime:
            result.append([f(j)])
        else:
            result[i].append(j)

In order to make it less verbose an more elegant, I thought I will preallocate a list of empty lists

result = createListOfEmptyLists(x)
for i in range(x):
    for j in someListOfElements:
        result[i].append(j)

The preallocation part isn't obvious to me. When I do result = [[]] * x, I receive a list of x references to the same list, so that the output of the following

result[0].append(10)
print result

is:

[[10], [10], [10], [10], [10], [10], [10], [10], [10], [10]]

I can use a loop (result = [[] for i in range(x)]), but I wonder whether a "loopless" solution exists.

Is the only way to get what I'm looking for

alain.janinm
  • 19,951
  • 10
  • 65
  • 112
Boris Gorelik
  • 29,945
  • 39
  • 128
  • 170
  • There are probably even better ways of doing all this, but it's hard to tell with only this code. – Ignacio Vazquez-Abrams Oct 07 '10 at 08:52
  • May I ask, what's wrong with list comprehension? I don't think that list multiplication is more elegant or (much) more faster (although it can be faster as there is not instancing while multiplying lists). – AkiRoss Oct 07 '10 at 09:03
  • For a list of empty lists, there's `[ [ [] for c in range(cols) ] for r in range(rows) ]`. – Tony Jan 20 '14 at 23:24

5 Answers5

53
result = [list(someListOfElements) for _ in xrange(x)]

This will make x distinct lists, each with a copy of someListOfElements list (each item in that list is by reference, but the list its in is a copy).

If it makes more sense, consider using copy.deepcopy(someListOfElements)

Generators and list comprehensions and things are considered quite pythonic.

Will
  • 73,905
  • 40
  • 169
  • 246
  • +1: The list comprehension is not the _only_ solution, just the most _pythonic_ and I daresay most __correct__ solution. – Kimvais Oct 07 '10 at 09:21
  • 5
    To populate a list with `n` empty lists, do `result = [[] for _ in xrange(n)]` – gsamaras Feb 01 '16 at 21:46
  • 1
    For python3, result = `[someListOfElements for _ in range(desired_length)]` works. – stephenb Jul 26 '17 at 14:45
  • @stephenb that's valid python, but its not doing the same thing. Its making a list where each element points to the someListOfElements, rather than copies of them. – Will Jul 26 '17 at 15:18
  • For the solution given by @gsamaras above, if using in Python 3 use: `result = [[] for _ in range(n)]` since `xrange` has been deprecated and does not work. – salvu Sep 21 '17 at 12:10
7

There's not really a way to create such a list without a loop of some sort. There are multiple ways of hiding the loop, though, just like [[]] * x hides the loop. There's the list comprehension, which "hides" the loop in an expression (bit it's thankfully still obvious.) There's also map(list, [[]]*x) which has two hidden loops (the one in [[]] * x and the one in map that creates a copy of each list using list().)

There's also the possibility of not creating the list of lists beforehand. The other answers already cover the simple approach, but if that somehow doesn't fit your needs there are other ways. For example, you could make a function that append an empty list to the result list as necessary, and call that:

def append(L, idx, item):
    while len(L) <= idx:
        L.append([])
    L[idx].append(item)

for i in range(x):
    for j in someListOfElements:
        append(result, i, j)

Or you could use a collections.defaultdict(list) instead of a list:

import collections
result = collections.defaultdict(list)
for i in range(x):
    for j in someListOfElements:
        result[i].append(j)

That has the benefit of using an already existing type, which is less work, but it does mean you now have a dict (indexed by integers) instead of a list, which may or may not be what you want. Or you could make a class that behaves almost like a list but appends new lists to itself instead of raising IndexError, such as:

import UserList
class defaultlist(UserList.UserList):
    def __getitem__(self, idx):
        while len(self) <= idx:
            self.append([])
        return UserList.UserList.__getitem__(self, idx)

result = defaultlist()
for i in range(x):
    for j in someListOfElements:
        result[i].append(j)
Thomas Wouters
  • 130,178
  • 23
  • 148
  • 122
  • 1
    Do note that `[[]] * count` does _not_ create a list of empty lists. It creates a list of references to the same one empty list. Adding an entry to one of those lists, adds them to all of the lists. – Jeroen Nov 29 '19 at 20:53
5

You could write a quick generator function. This would have uses other than this particular case so I'll generalize it a little. Dig this:

def create(n, constructor=list):
    for _ in xrange(n):
        yield constructor()

Then to make a list of lists,

result = list(create(10))

to make a list of empty dicts,

result = list(create(20, dict))

and (for the sake of completeness) to make a list of empty Foos,

result = list(create(30, Foo))

Of course, you could also make a tuple of any of the above. It wouldn't be too hard to extend it to allow arguments to constructor either. I would probably have it accept a function which accepted an index and returned the arguments to be passed to the constructor.

One last thought is that, because the only requirement that we are placing on constructor is that it be a callable, you could even pass it anything that returns what you want in your list. A bound method that pulls results out of a database query for instance. It's quite a useful little three lines of code.

aaronasterling
  • 68,820
  • 20
  • 127
  • 125
4

Why not keep it simple by just appending the list in appropriate loop

result = []
for i in range(x):
    result.append([])
    for j in someListOfElements:
        result[i].append(j)

[Edit: Adding example]

>>> someListOfElements = ['a', 'b', 'c']
>>> x = 3
>>> result = []
>>> for i in range(x):
...     result.append([])
...     for j in someListOfElements:
...         result[i].append(j)
... 
>>> 
>>> result
[['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'b', 'c']]
pyfunc
  • 65,343
  • 15
  • 148
  • 136
0

Please include runnable sample code, so we can run the code ourself to quickly see exactly what it is you want to do. It looks like you just want this:

result = []
for i in range(x):
    data = []
    for j in someListOfElements:
        data.append(j)
    # or data = [j for j in someListOfElements]
    result.append(data)
Glenn Maynard
  • 55,829
  • 10
  • 121
  • 131