I'm wondering if it is possible to define a range of elements within a list definition by list comprehension. I would like to achieve something like this as a result:
>> ['a', 'b', 1, 2, 3, 'c']
I tried this:
result_list = ['a', 'b', [i for i in range(3)] 'c']
But this yields ['a', 'b', [1, 2, 3], 'c']
Or if i try with tuple, I get a generator object like so: ['a', 'b', <generator object <genexpr> at 0x0000000002C9A9E8>, 'c']
Can it be done somehow?
Thanks!
This is what you need:
result_list = ['a', 'b', *[i for i in range(3)], 'c']
print(result_list) # ['a', 'b', 0, 1, 2, 'c']
In recent versions of Python 3 you can use star unpacking inside the list definition:
result_list = ['a', 'b', *[i for i in range(3)], 'c']
print(result_list)
output
['a', 'b', 0, 1, 2, 'c']
Bear in mind that it can get messy if you mix data types inside a list. If you want a heterogeneous collection, it's generally better to use a tuple.
Also note that while
[[i for i in range(u)] for u in range(3)]
is valid, and will produce
[[], [0], [0, 1]]
you can't get a flattened version by doing
[*[i for i in range(u)] for u in range(3)]
If you're stuck on an older version of Python, your best option is to use itertools.chain.from_iterable:
import itertools
src = ['a', 'b', [i for i in range(3)], 'c']
result_list = list(itertools.chain.from_iterable(src))
result_list = ['a', 'b'] + [i for i in range(1, 4)] + ['c']
As documented in https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists you can unpack lists as arguments using the * character.
So your answer would be:
result_list = ['a', 'b', *[i for i in range(3)], 'c']
Related
I'm attempting to interleave a value into a list.
interleave(1, ['A', 'B', 'C']) -> [1, 'A', 1, 'B', 1, 'C']
There are a number of ways to solve this problem, but I thought more_itertools.interleave would be the most readable. However to use this function I need to create a generator that yields 1 forever. Clearly this can be done with
def gen1():
while True:
yield 1
but this feels more complicated than necessary.
Is there an elegant way to replace ... in the snippet below to create an inline generator such that
res = more_itertools.interleave(..., ['A', 'B', 'C'])
assert list(res) == [1, 'A', 1, 'B', 1, 'C']
... = (1 for _ in ['A', 'B', 'C']) clearly works, but I wouldn't call it elegant.
itertools.repeat. I don't know how interleave is implemented, but you can do the same thing with chain and zip:
from itertools import chain, repeat
print(list(chain.from_iterable(zip(repeat(1), ['A', 'B', 'C']))))
# [1, 'A', 1, 'B', 1, 'C']
You can also use a list comprehension like this:
def interleave(value, lst):
return [j for i in lst for j in (value, i)]
So that interleave(1, ['A', 'B', 'C']) returns:
[1, 'A', 1, 'B', 1, 'C']
def interleave(a,list1):
newlist=[]
for x in list1:newlist.extend([a,x])
return newlist
print(interleave(1,['A','B','C']))
prints
[1, 'A', 1, 'B', 1, 'C']
Really stupid question as I am new to python:
If I have labels = ['a', 'b', 'c', 'd'],
and indics = [2, 3, 0, 1]
How should I get the corresponding label using each index so I can get: ['c', 'd', 'a', 'b']?
There are a few alternatives, one, is to use a list comprehension:
labels = ['a', 'b', 'c', 'd']
indices = [2, 3, 0, 1]
result = [labels[i] for i in indices]
print(result)
Output
['c', 'd', 'a', 'b']
Basically iterate over each index and fetch the item at that position. The above is equivalent to the following for loop:
result = []
for i in indices:
result.append(labels[i])
A third option is to use operator.itemgetter:
from operator import itemgetter
labels = ['a', 'b', 'c', 'd']
indices = [2, 3, 0, 1]
result = list(itemgetter(*indices)(labels))
print(result)
Output
['c', 'd', 'a', 'b']
Is there a Python builtin that repeats each element of a list based on the corresponding value in another list? For example A in list x position 0 is repeated 2 times because of the value 2 at position 0 in the list y.
>>> x = ['A', 'B', 'C']
>>> y = [2, 1, 3]
>>> f(x, y)
['A', 'A', 'B', 'C', 'C', 'C']
Or to put it another way, what is the fastest way to achieve this operation?
Just use a simple list comprehension:
>>> x = ['A', 'B', 'C']
>>> y = [2, 1, 3]
>>> [x[i] for i in range(len(x)) for j in range(y[i])]
['A', 'A', 'B', 'C', 'C', 'C']
>>>
One way would be the following
x = ['A', 'B', 'C']
y = [2, 1, 3]
s = []
for a, b in zip(x, y):
s.extend([a] * b)
print(s)
result
['A', 'A', 'B', 'C', 'C', 'C']
from itertools import chain
list(chain(*[[a] * b for a, b in zip(x, y)]))
['A', 'A', 'B', 'C', 'C', 'C']
There is itertools.repeat as well, but that ends up being uglier for this particular case.
Try this
x = ['A', 'B', 'C']
y = [2, 1, 3]
newarray = []
for i in range(0,len(x)):
newarray.extend(x[i] * y[i])
print newarray
I have a list of lists from which I would like to remove duplicates and sum up duplicates' last elements. An item is a duplicate if its first 2 elements are the same. This is better illustrated with an example:
input = [['a', 'b', 2], ['a', 'c', 1], ['a', 'b', 1]]
# Desired output
output = [['a', 'b', 3], ['a', 'c', 1]]
There are similar questions here on SO but I haven't found one which would deal with list of lists and summing up list items at the same time.
I tried several approaches but couldn't make it work:
create a copy of input list, make a nested loop, if second duplicate is found, add its last item to original --> this got too confusing with too much nesting
I looked into collections Counter but it doesn't seem to work with list of lists
itertools
Could you give me any pointers on how to approach this problem?
I don't think lists are the best data structure for it. I would use dictionaries with tuple key. I you really need list, you can create one later:
from collections import defaultdict
data = [['a', 'b', 2], ['a', 'c', 1], ['a', 'b', 1]]
result = collections.defaultdict(int) # new keys are auto-added and initialized as 0
for item in data:
a, b, value = item
result[(a,b)] += value
print result
# defaultdict(<type 'int'>, {('a', 'b'): 3, ('a', 'c'): 1})
print dict(result)
# {('a', 'b'): 3, ('a', 'c'): 1}
print [[a, b, total] for (a, b), total in result.items()]
# [['a', 'b', 3], ['a', 'c', 1]]
You could use Counter; someone's already given a manual defaultdict solution; so here's an itertools.groupby one, just for variety:
>>> from itertools import groupby
>>> inp = [['a', 'b', 2], ['a', 'c', 1], ['a', 'b', 1]]
>>> [k[:2] + [sum(v[2] for v in g)] for k,g in groupby(sorted(inp), key=lambda x: x[:2])]
[['a', 'b', 3], ['a', 'c', 1]]
but I second #m.wasowski's view that a dictionary (or dict subclass like defaultdict or Counter) is probably a better data structure.
It'd also be somewhat more general to use [:-1] and [-1] instead of [:2] and [2], but I'm too lazy to make the change. :-)
I prefer this approach:
>>> from collections import Counter
>>> from itertools import repeat, chain
>>> sum((Counter({tuple(i[:-1]): i[-1]}) for i in input), Counter())
Counter({('a', 'b'): 3, ('a', 'c'): 1})
(Thanks to #DSM for pointing out an improvement to my original answer.)
If you want it in list form:
>>> [[a, b, n] for (a,b),n in _.items()]
[['a', 'b', 3], ['a', 'c', 1]]
>>> t = [['a', 'b', 2], ['a', 'c', 1], ['a', 'b', 1]]
>>> sums = {}
>>> for i in t:
sums[tuple(i[:-1])] = sums.get(tuple(i[:-1]),0) + i[-1]
>>> output = [[a,b,sums[(a,b)]] for a,b in sums]
>>> output
[['a', 'b', 3], ['a', 'c', 1]]
inp = [['a', 'b', 2], ['a', 'c', 1], ['a', 'b', 1], ['a', 'c', 2], ['a', 'b', 4]]
lst = []
seen = []
for i, first in enumerate(inp):
if i in seen:
continue
found = False
count = first[-1]
for j, second in enumerate(inp[i + 1:]):
if first[:2] == second[:2]:
count += second[-1]
found = True
seen.append(i + j + 1)
if found:
lst.append(first[:-1] + [count])
else:
lst.append(first)
print(lst)
# [['a', 'b', 7], ['a', 'c', 3]]
Here is a input list:
['a', 'b', 'b', 'c', 'c', 'd']
The output I expect should be:
[[0, 'a'], [1, 'b'], [1, 'b'], [2, 'c'], [2, 'c'], [3, 'd']]
I try to use map()
>>> map(lambda (index, word): [index, word], enumerate([['a', 'b', 'b', 'c', 'c', 'd']])
[[0, 'a'], [1, 'b'], [2, 'b'], [3, 'c'], [4, 'c'], [5, 'd']]
How can I get the expected result?
EDIT: This is not a sorted list, the index of each element increase only when meet a new element
>>> import itertools
>>> seq = ['a', 'b', 'b', 'c', 'c', 'd']
>>> [[i, c] for i, (k, g) in enumerate(itertools.groupby(seq)) for c in g]
[[0, 'a'], [1, 'b'], [1, 'b'], [2, 'c'], [2, 'c'], [3, 'd']]
[
[i, x]
for i, (value, group) in enumerate(itertools.groupby(['a', 'b', 'b', 'c', 'c', 'd']))
for x in group
]
It sounds like you want to rank the terms based on a lexicographical ordering.
input = ['a', 'b', 'b', 'c', 'c', 'd']
mapping = { v:i for (i, v) in enumerate(sorted(set(input))) }
[ [mapping[v], v] for v in input ]
Note that this works for unsorted inputs as well.
If, as your amendment suggests, you want to number items based on order of first appearance, a different approach is in order. The following is short and sweet, albeit offensively hacky:
[ [d.setdefault(v, len(d)), v] for d in [{}] for v in input ]
When list is sorted use groupby (see jamylak answer); when not, just iterate over the list and check if you've seen this letter already:
a = ['a', 'b', 'b', 'c', 'c', 'd']
result = []
d = {}
n = 0
for k in a:
if k not in d:
d[k] = n
n += 1
result.append([d[k],k])
It is the most effective solution; it takes only O(n) time.
Example of usage for unsorted lists:
[[0, 'a'], [1, 'b'], [1, 'b'], [2, 'c'], [2, 'c'], [3, 'd'], [0, 'a']]
As you can see, you have here the same order of items as in the input list.
When you sort the list first you need O(n*log(n)) additional time.