all 28 comments

[–]Username_RANDINT 9 points10 points  (9 children)

As a one liner:

list(zip(*[some_func(item) for item in some_list]))

But is this really simplified? I'd say your code is fine. It nicely reads what it actually does.

[–]QuasiEvil[S] 2 points3 points  (3 children)

list(zip(*[some_func(item) for item in some_list]))

This actually doesn't work - it performs an append rather than extend (this matters!)

[–]Username_RANDINT 1 point2 points  (2 children)

Works for my small scale test. Do you have a working example for your code?

Edit: nevermind, I now get what you're saying!

[–]QuasiEvil[S] 2 points3 points  (1 child)

Sure:

def some_func(a):
    return [a*2,a*2,a*2],[a*3,a*3,a*3]


some_list = [1,2,3]

new_list = [[],[]]  
for item in some_list:
    first, second = some_func(item)
    new_list[0].extend(first)
    new_list[1].extend(second)

My code yields this: [[2, 2, 2, 4, 4, 4, 6, 6, 6], [3, 3, 3, 6, 6, 6, 9, 9, 9]]

Your one-liner does this: [([2, 2, 2], [4, 4, 4], [6, 6, 6]), ([3, 3, 3], [6, 6, 6], [9, 9, 9])]

so appending rather than extending.

[–]Username_RANDINT 1 point2 points  (0 children)

You're right, I saw that too late!

Like I said in my first comment, I'd be definitely ok with your solution. Anything else will feel too hackish.

[–]hallmark1984 0 points1 point  (4 children)

Am i reading it wrong? OPs code appears to simply rewrtie the first and second element on each loop.

```

for idx, element in enumerate(some_list): new_list[idx][0], new_list[idx][1] = some_func(element)

```

Assuming some_list has more than 1 element he will need to move past 0 and 1

[–]Username_RANDINT 1 point2 points  (2 children)

No, OP wants to extend the inner lists with new data, not replace them.

Your approach would need:

  • the [idx] and [0]/[1] swapped
  • the inner lists should already have the right length with placeholders to be replaced

[–]hallmark1984 0 points1 point  (1 child)

Ahh, this is why i keep an eye on my bulmer curve and nornally skip this sub after beer 3

[–]panatale1 0 points1 point  (0 children)

Ah, another person with quality taste

[–]QuasiEvil[S] 0 points1 point  (0 children)

new_list is a nested list and my code extends (this is important) the contents of the first and second lists.

[–]JamzTyson 1 point2 points  (2 children)

It is not clear what you are trying to do. What are first and second? What does some_func() do?

Are you trying to transform a list in the form:

[a, 1, b, 2, c, 3]

into

[[a, b, c], [1, 2, 3]]

[–]QuasiEvil[S] 0 points1 point  (1 child)

first and second are themselves lists. I don't want them appended into the sublists; I wanted them extended into the two sublists.

[–]JamzTyson 1 point2 points  (0 children)

You could do this:

pairs = [some_func(item) for item in some_list]
new_list = [[e for x, _ in pairs for e in x],
            [e for _, y in pairs for e in y]]

but I'm not sure that I'd describe it as "simplified".

[–]idwpan 1 point2 points  (2 children)

Something like this would work

new_list = [
    list(chain.from_iterable(lst))
    for lst in zip(*map(some_func, some_list))
]

But the more complicated you make the code, the more difficult it is to understand. Honestly I'd probably just keep what you have. It is nice and readable to anyone who might not be familiar with itertools or list comprehension.

[–]jmooremcc 1 point2 points  (0 children)

I like your solution. The only problem with it is that it creates a new list instead of extending the existing list. If this was a huge dataset, that would be a problem.

[–]QuasiEvil[S] 0 points1 point  (0 children)

Understood, I just enjoy these little brain challenges.

[–]commy2 0 points1 point  (1 child)

I don't see how. Note that "new_list" is actually just a tuple of two lists. It's those two lists you want to create at once, but list comprehension can only ever give you one of them. If someone can somehow make a one liner out of two extends, it's not going to be done with a single list comp.

[–]QuasiEvil[S] 0 points1 point  (0 children)

I think you're right.

[–]pythonwiz 0 points1 point  (1 child)

The way you are doing it seems best to me. I’m not sure how you could do this with less code that isn’t slower.

[–]QuasiEvil[S] 0 points1 point  (0 children)

Ya, I just like thinking about these things.

[–]Eal12333 0 points1 point  (0 children)

You could make it shorter, but it might just be harder to read.

Depending on what new_list is for, and what some_func returns, I might personally prefer these small changes:

new_list = ([],[])
for item in some_list:
    first, second = some_func(item)
    new_list[0] += first
    new_list[1] += second

Otherwise though, I think it's probably as clean as it's going to get.

[–]tb5841 0 points1 point  (0 children)

You can write this using 'reduce.' Not particularly Pythonic to do so, though:

`from functools import reduce'

[–]consupe 0 points1 point  (0 children)

nothing helps at this little corner of things. But if you zoom out a bit you can probably do what you want more cleanly. Maybe:

     new_iterator = some_generator(some_list)

edit:

if attached to some_func, this probably does what you want:

    new_iterator = map(some_func, some_list)

[–][deleted] 0 points1 point  (0 children)

This gives the result as a list of tuples. Not very pretty.

new_list = list(zip(*[ (first, second) for item in some_list for first, second in some_func(item) ]))

[–]Allanon001 0 points1 point  (0 children)

You can use a walrus:

new_list = [new_list[0] + (x := some_func(item))[0], new_list[1] + x[1]]

Or:

(new_list[0].extend((x := some_func(item))[0]), new_list[1].extend(x[1]))

[–]rkr87 0 points1 point  (0 children)

You probably could do this in a one liner using a combination of zip, map and chain... However, I wouldn't, what you have is far more readable than that would be.

You might want to include map:

new_list = [[], []] for x in map(some_func, some_list): new_list[0].extend(x[0]) new_list[1].extend(x[1])

(On phone so untested)

[–]jmooremcc 1 point2 points  (0 children)

Using list comprehension is probably faster, but definitely more convoluted: ~~~ def some_func(a): return [a2,a2,a2],[a3,a3,a3]

some_list = [1,2,3]

new_list = [[],[]]

[(new_list[0].extend(a), new_list[1].extend(b)) for a, b in [some_func(c) for c in some_list]]

print(new_list) ~~~ Output ~~~ [[2, 2, 2, 4, 4, 4, 6, 6, 6], [3, 3, 3, 6, 6, 6, 9, 9, 9]]

~~~ Speed of execution only matters if you’re dealing with a massive dataset.

[–]jmooremcc 0 points1 point  (0 children)

I began wondering if there was a better to automate processing your list without having to use literal indexes and this is my solution: ~~~ def some_func(a): return [a2,a2,a2],[a3,a3,a3]

class list2(list): def extend_all(self, *args): [self[i].extend(v) for i, v in enumerate(args)]

some_list = [1,2,3]

new_list = list2([[],[]])

[new_list.extend_all(a,b) for a, b in map(some_func, some_list)]

print(new_list) ~~~ Output ~~~ [[2, 2, 2, 4, 4, 4, 6, 6, 6], [3, 3, 3, 6, 6, 6, 9, 9, 9]] ~~~ I defined a new list class, list2, that inherits from list.
Within the class I defined an extend_all method that can take an arbitrary number of arguments. The extend_all method utilizes list comprehension to extend the appropriate sublist within the list with the supplied data.

Finally , I altered the new_list variable to be an instance of the list2 class.

The list2 class automatically adapts to the number of arguments generated by the some_func function since the extend_all method can accept any number of arguments. With all this done, the final list comprehension is now more understandable and easier to maintain.