views:

96

answers:

6

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

A: 

Try using list() instead of [], I think that would create a new instance.

AkiRoss
Nope, the problem isn't that `[]` doesn't create a new instance, the problem is that list-repetition through multiplication *repeats* the list contents, rather than copying it.
Thomas Wouters
Yeah, in fact I do use list comprehension for this task, and not multiplication.
AkiRoss
+1  A: 

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
`list([])` is a little redundant.
Thomas Wouters
@Thomas Wouters : Thanks, edited and corrected.
pyfunc
A: 

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
+2  A: 

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
+2  A: 

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
+3  A: 
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
+1: The list comprehension is not the _only_ solution, just the most _pythonic_ and I daresay most __correct__ solution.
Kimvais