I have a list of mixed tuples:
my_list = [("Adam",1985),("Karen",1990),("anna",1986)]
How to unpack it separately into two lists or tuples, one with strings and one with integers?
I have a list of mixed tuples:
my_list = [("Adam",1985),("Karen",1990),("anna",1986)]
How to unpack it separately into two lists or tuples, one with strings and one with integers?
(str, int)
Using python's zip
you can easily unpack list of tuples, without any particular regard to types:
my_list = [("Adam",1985),("Karen",1990),("anna",1986)]
names, years = zip(*my_list)
The unpacked lists are:
('Adam', 'Karen', 'anna'), (1985, 1990, 1986)
*
operatorThe *
operator is called Unpacking Argument Lists, it passes the single elements of the list as argument. Suppose you have a list with 3 elements my_list = [1, 2, 3]
ad a function with 3 parameters def func(a, b, c)
: you can use func(*my_list)
to call func(my_list[0], my_list[1], ..., my_list[n])
.
Using functools.cmp_to_key
we can first sort the tuples and then execute the zip:
import functools
key=functools.cmp_to_key(lambda x1, x2: 1 if isinstance(x2, str) else -1)
names, years = zip(*[sorted(t, key=key) for t in l])
This way the list is first sorted as:
[['Adam', 1985], ['Karen', 1990], ['anna', 1986]]
And afterwards running the zip we obtain the same result:
('Adam', 'Karen', 'anna'), (1985, 1990, 1986)
from itertools import chain, groupby
names, years = [
list(v) for _, v in groupby(
sorted(chain.from_iterable(my_list), key=lambda x: str(x)),
lambda x: isinstance(x, str))
]
Running timeit on the example list we get:
Tuple sorting:
%timeit zip(*[sorted(t, key=key) for t in my_list])
>>> 5.2 µs ± 157 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Groupby
%timeit [list(v) for _, v in groupby(sorted(chain.from_iterable(my_list), key=lambda x: str(x)), lambda x: isinstance(x, str))]
>>> 6.83 µs ± 358 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Running timeit on the a large list such as:
import random
n = 100000
my_list = []
for i in range(n):
if random.choice([True, False]):
my_list.append((i, "Karen"))
else:
my_list.append(("Karen", i))
Tuple sorting:
%timeit zip(*[sorted(t, key=key) for t in my_list])
>>> 166 ms ± 8.99 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Groupby
%timeit [list(v) for _, v in groupby(sorted(chain.from_iterable(my_list), key=lambda x: str(x)), lambda x: isinstance(x, str))]
>>> 149 ms ± 2.62 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Even though on small list the first method seems to be the faster one, on larger list the groupby method appears faster.
Using zip
>>> years, names = zip(*(sorted(t, key=lambda x: str(type(x))) for t in my_list))
>>> years
(1985, 1990, 1986)
>>> names
('Adam', 'Karen', 'anna')
>>>
Or using dict
>>> d = dict(sorted(t, key=lambda x: str(type(x))) for t in my_list)
>>> years = tuple(d.keys())
>>> names = tuple(d.values())
>>>
>>> years
(1985, 1990, 1986)
>>> names
('Adam', 'Karen', 'anna')
>>>
you can convert it into dictionary and work as normal
dict([("Adam",1985),("Karen",1990),("anna",1986)])
{'Adam': 1985, 'Karen': 1990, 'anna': 1986}