3

I've a function returning list from list of lists where the return list groups members of each list by index numbers. Code and example:

def listjoinervar(*lists: list) -> list:
    """returns list of grouped values from each list 
        keyword arguments:
        lists: list of input lists
    """ 
    assert(len(lists) > 0) and (all(len(i) == len(lists[0]) for i in lists))
    joinedlist = [None] * len(lists) * len(lists[0])
    for i in range(0, len(joinedlist), len(lists)):
        for j in range(0, len(lists[0])):
            joinedlist[i//len(lists[0]) + j*len(lists[0])] = lists[i//len(lists[0])][j]
    return joinedlist

a = ['a', 'b', 'c']
b = [1, 2, 3]
c = [True, False, False]
listjoinervar(a, b, c)
# ['a', 1, True, 'b', 2, False, 'c', 3, False]

Are there ways to make this more Pythonic using itertools, generators, etc? I've looked at examples like this but in my code there is no interaction b/w the elements of the individual lists. Thanks

Underoos
  • 4,708
  • 8
  • 42
  • 85
shanlodh
  • 1,015
  • 2
  • 11
  • 30

4 Answers4

7

Use itertools.chain.from_iterable + zip:

from itertools import chain

def listjoinervar(*a):
    return list(chain.from_iterable(zip(*a)))

Usage:

>>> a = ['a', 'b', 'c']
>>> b = [1, 2, 3]
>>> c = [True, False, False]
>>> listjoinervar(a, b, c)
['a', 1, True, 'b', 2, False, 'c', 3, False]
Austin
  • 25,759
  • 4
  • 25
  • 48
3

In a normal situation, I would also use itertools.chain, as in Austin's answer.

However, just for completeness, an alternative solution that does not import anything:

def join_lists(*a):
    return [element for sub in zip(*a) for element in sub]

a = ['a', 'b', 'c']
b = [1, 2, 3]
c = [True, False, False]

join_lists(a, b, c)

Output:

['a', 1, True, 'b', 2, False, 'c', 3, False]
gmds
  • 19,325
  • 4
  • 32
  • 58
  • Thanks, could you please explain why in a 'normal situation' you'd use itertools.chain when it seems this can be done without it? Are there any other benefits of itertools.chain? – shanlodh Apr 13 '19 at 09:31
  • @shanlodh No, they are effectively the same. However, I personally find `chain.from_iterable(zip(*[a, b, c]))` slightly more readable than the comprehension approach. – gmds Apr 13 '19 at 09:34
  • Thanks, I'm accepting this answer because list comprehensions can be [efficient](https://stackoverflow.com/questions/10632839/transform-list-of-tuples-into-a-flat-list-or-a-matrix) in these cases w/o additional dependencies – shanlodh Apr 13 '19 at 09:39
  • @shanlodh, you might also be interested in: https://stackoverflow.com/questions/49631326/why-is-itertools-chain-faster-than-a-flattening-list-comprehension – Austin Apr 13 '19 at 10:23
  • @Austin: thanks, interesting link particularly zwer's comments under the link's OP which mirrors my experience in this case. My OP func seems to be running faster than the more Pythonic solutions in actual code – shanlodh Apr 13 '19 at 12:40
1

Using zip and list comprehension:

from typing import List, Any

def listjoinervar(*args: List[Any]) -> List[Any]:
    return [item for sublist in list(zip(*args)) for item in sublist]

Usage:

>>> a = ["a", "b", "c"]
>>> b = [1, 2, 3]
>>> c = [True, False, False]
>>> listjoinervar(a,b,c)
['a', 1, True, 'b', 2, False, 'c', 3, False]

The use of type annotations is optional.

Snorfalorpagus
  • 3,348
  • 2
  • 29
  • 51
0

You can do this without having to import anything, using the enumerate and max methods:

def custom_group(*args):
  biggest, result = max(args, key = lambda x: len(x)), []
  for (i, value) in enumerate(biggest):
    for arr in args:
      if len(arr) > i:
        result.append(arr[i])
  return result

You're looping through the biggest list and adding each value from each list at that index (if it exists) to the results list then moving on to the next index until the loop stops.

Using your specified arrays:

a = ["a", "b", "c"]
b = [1, 2, 3]
c = [True, False, False]

You'd call the function, like this:

print(custom_group(a,b,c))

Which should result in the following list being outputted:

["a", 1, True, "b", 2, False, "c", 3, False]

Good luck.

Malekai
  • 4,765
  • 5
  • 25
  • 60