If I created a list:
_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
and tried to get every value <= 6 by using a generator:
test = next(i for i in a for a in _list if i <= 6)
(btw this doesn't work ↑↑↑↑↑)
How do I iterate through lists within a list using a generator? If this is not possible, what method can I use in place of this?
I have looked here:
python generator of generators?, but could not find an answer...
Using chain from builtin module itertools:
from itertools import chain
_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(list(i for i in chain.from_iterable(_list) if i <= 6))
Output:
[1, 2, 3, 4, 5, 6]
What itertools.chain does? According manual pages:
Make an iterator that returns elements from the first iterable until
it is exhausted, then proceeds to the next iterable, until all of the
iterables are exhausted. Used for treating consecutive sequences as a
single sequence.
in that case, you don't need to create a generator of generator. Just create a double for loop in one generator (to flatten the list, then test for the element values):
_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
gen = (x for sublist in _list for x in sublist if x <= 6)
this double for comprehension syntax needs some using to, comprehensions are generally written "the other way round", but not in the case of loops, hence your confusion. A more general form would be:
(expression(item) for sublist in input_list for item in sublist if condition(item))
then to get the first matching element:
print(next(gen,None)) # None is printed if there's no matching element
(to get all the elements, of course you have to iterate on gen, next provides the "next" values, like if you're iterating "manually" on your generator)
to nest two fors in the same generator you must reverse the order
test = next(i for a in _list for i in a if i <= 6)
You can combine itertools.takewhile and itertools.chain to create an iterator matching your requirements
>>> from itertools import chain, takewhile
>>> itr = takewhile(lambda i: i<=6, chain(*_list))
>>> print (list(itr))
[1, 2, 3, 4, 5, 6]
>>> t = (i for a in _list for i in a if i <= 6)
>>> next(t)
1
>>> next(t)
2
>>> next(t)
3
>>> next(t)
4
>>> next(t)
5
>>>
Use list to convert the iterator to a list
>>> t = (i for a in _list for i in a if i <= 6)
>>> list(t)
[1, 2, 3, 4, 5, 6]
Related
I am having issue figuring out a way to shuffle two python list.
I have two different lists.
first = [1,2,3]
second = [4,5,6]
I want my final list to be a combination of these two lists but shuffled in a particular way.
combined = [1,4,2,5,3,6]
I can shuffle both the lists and combine them, but the result would be [2,1,3,6,5,4] but what I want is [1,4,2,5,3,6].
The combined list should have one item from the first list, and then the subsequent item from the second list.
The two lists might even be of different lengths.
first = [1,2,3]
second = [4,5,6,7]
def shuffle(f, s):
newlist = []
maxlen = len(f) if len(f) > len(s) else len(s)
for i in range(maxlen):
try:
newlist.append(f[i])
except IndexError:
pass
try:
newlist.append(s[i])
except IndexError:
pass
return newlist
print(shuffle(first, second))
If both lists are always going to be the same length then you could do:
x = [1, 2, 3]
y = [4, 5, 6]
z = []
for i in range(len(x)):# Could be in range of y too as they would be the same length
z.append(x[i])
z.append(y[i])
If they are different lengths then you will have to do things slightly differently and check which one is longer and make that the one you get the length of. The you will need to check each iteration of the loop if you are past the length of the shorter one.
Purely because I want to understand itertools one day:
from itertools import chain, zip_longest, filterfalse
first = [1, 2, 3]
second = [4, 5, 6, 9, 10, 11]
result = filterfalse(
lambda x: x is None,
chain.from_iterable(zip_longest(first, second)))
print(tuple(result))
Enjoy!
For lists of unequal length, use itertools.zip_longest:
from itertools import chain, zip_longest
a = [1, 2, 3]
b = [4, 5, 6, 7]
combined = [x for x in chain(*zip_longest(a, b)) if x is not None]
print(combined) # -> [1, 4, 2, 5, 3, 6, 7]
For Python 2, replace zip_longest with izip_longest.
This is based on pylang's answer on Interleave multiple lists of the same length in Python
Here it is generalized, as a function in a module.
#!/usr/bin/env python3
from itertools import chain, zip_longest
def interleave(*args):
"""
Interleave iterables.
>>> list(interleave([1, 2, 3], [4, 5, 6, 7]))
[1, 4, 2, 5, 3, 6, 7]
>>> ''.join(interleave('abc', 'def', 'ghi'))
'adgbehcfi'
"""
for x in chain(*zip_longest(*args)):
if x is not None:
yield x
How would you iterate through a list of lists, such as:
[[1,2,3,4], [5,6], [7,8,9]]
and construct a new list by grabbing the first item of each list, then the second, etc. So the above becomes this:
[1, 5, 7, 2, 6, 8, 3, 9, 4]
You can use a list comprehension along with itertools.izip_longest (or zip_longest in Python 3)
from itertools import izip_longest
a = [[1,2,3,4], [5,6], [7,8,9]]
[i for sublist in izip_longest(*a) for i in sublist if i is not None]
# [1, 5, 7, 2, 6, 8, 3, 9, 4]
As long as you know that None will not appear, you can take advantage of how map() works with no function:
outlist = [y for sub in map(None, *inlist) for y in sub if not y is None]
DRY: itertools has a recipe that sounds right for this task: roundrobin
from itertools import cycle, islice
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
pending = len(iterables)
nexts = cycle(iter(it).next for it in iterables)
while pending:
try:
for next in nexts:
yield next()
except StopIteration:
pending -= 1
nexts = cycle(islice(nexts, pending))
l = [[1,2,3,4], [5,6], [7,8,9]]
print (list(roundrobin(*l)))
#[1, 5, 7, 2, 6, 8, 3, 9, 4]
You can do it manually without using any import if you don't want to.
Keep N different counters where n is the number of lists in your list and increment them whenever you add a new element from that sublists. While adding them you should control that respective sets counter must be the minimum of all other counters if all lists have remaining elements.When you add the last element of list you can increment its counter to something like 9999 to protect our minimum rule.
This will be harder to implement and I do not suggest you to implement in this way but this is also a possibility if you do not want to import anything or want to have a programming challenge for your level.
Solutions that filter out None won't work if None is a value from the sublists that you would like to keep. To fix this, you can do a list comprehension that iterates over the indices of the longest sublist, and only adds sublist items if the index is in range.
a = [[1,2,3,4],[5,6,None],[7,8,9]]
range_longest = range(max(map(len, a)))
[sublist[i] for i in range_longest for sublist in a if i < len(sublist)]
# [1, 5, 7, 2, 6, 8, 3, None, 9, 4]
The following reverses a list "in-place" and works in Python 2 and 3:
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[:] = reversed(mylist)
>>> mylist
[5, 4, 3, 2, 1]
Why/how? Since reversed gives me an iterator and doesn't copy the list beforehand, and since [:]= replaces "in-place", I am surprised. And the following, also using reversed, breaks as expected:
>>> mylist = [1, 2, 3, 4, 5]
>>> for i, item in enumerate(reversed(mylist)):
mylist[i] = item
>>> mylist
[5, 4, 3, 4, 5]
Why doesn't the [:] = fail like that?
And yes, I do know mylist.reverse().
CPython list slice assigment will convert the iterable to a list first by calling PySequence_Fast. Source: https://hg.python.org/cpython/file/7556df35b913/Objects/listobject.c#l611
v_as_SF = PySequence_Fast(v, "can only assign an iterable");
Even PyPy does something similar:
def setslice__List_ANY_ANY_ANY(space, w_list, w_start, w_stop, w_iterable):
length = w_list.length()
start, stop = normalize_simple_slice(space, length, w_start, w_stop)
sequence_w = space.listview(w_iterable)
w_other = W_ListObject(space, sequence_w)
w_list.setslice(start, 1, stop-start, w_other)
Here space.listview will call ObjSpace.unpackiterable to unpack the iterable which in turn returns a list.
I am trying to figure out how to append multiple values to a list in Python. I know there are few methods to do so, such as manually input the values, or put the append operation in a for loop, or the append and extend functions.
However, I wonder if there is a more neat way to do so? Maybe a certain package or function?
You can use the sequence method list.extend to extend the list by multiple values from any kind of iterable, being it another list or any other thing that provides a sequence of values.
>>> lst = [1, 2]
>>> lst.append(3)
>>> lst.append(4)
>>> lst
[1, 2, 3, 4]
>>> lst.extend([5, 6, 7])
>>> lst.extend((8, 9, 10))
>>> lst
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> lst.extend(range(11, 14))
>>> lst
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
So you can use list.append() to append a single value, and list.extend() to append multiple values.
Other than the append function, if by "multiple values" you mean another list, you can simply concatenate them like so.
>>> a = [1,2,3]
>>> b = [4,5,6]
>>> a + b
[1, 2, 3, 4, 5, 6]
If you take a look at the official docs, you'll see right below append, extend. That's what your looking for.
There's also itertools.chain if you are more interested in efficient iteration than ending up with a fully populated data structure.
if the number of items was saved in a variable say n. you can use list comprehension and plus sign for list expansion.
lst = ['A', 'B']
n = 1
new_lst = lst + ['flag'+str(x) for x in range(n)]
print(my_lst)
>>> ['A','B','flag0','flag1']
One way you can work around this type of problem is -
Here we are inserting a list to the existing list by creating a variable new_values.
Note that we are inserting the values in the second index, i.e. a[2]
a = [1, 2, 7, 8]
new_values = [3, 4, 5, 6]
a.insert(2, new_values)
print(a)
But here insert() method will append the values as a list.
So here goes another way of doing the same thing, but this time, we'll actually insert the values in between the items.
a = [1, 2, 7, 8]
a[2:2] = [3,4,5,6]
print(a)
I have a list like:
list = [[1,2,3],[4,5,6],[7,8,9]]
I want to append a number at the start of every value in the list programmatically, say the number is 9. I want the new list to be like:
list = [[9,1,2,3],[9,4,5,6],[9,7,8,9]]
How do I go about doing this in Python? I know it is a very trivial question but I couldn't find a way to get this done.
for sublist in thelist:
sublist.insert(0, 9)
don't use built-in names such as list for your own stuff, that's just a stupid accident in the making -- call YOUR stuff mylist or thelist or the like, not list.
Edit: as the OP aks how to insert > 1 item at the start of each sublist, let me point out that the most efficient way is by assignment of the multiple items to a slice of each sublist (most list mutators can be seen as readable alternatives to slice assignments;-), i.e.:
for sublist in thelist:
sublist[0:0] = 8, 9
sublist[0:0] is the empty slice at the start of sublist, and by assigning items to it you're inserting the items at that very spot.
>>> someList = [[1,2,3],[4,5,6],[7,8,9]]
>>> someList = [[9] + i for i in someList]
>>> someList
[[9, 1, 2, 3], [9, 4, 5, 6], [9, 7, 8, 9]]
(someList because list is already used by python)
Use the insert method, which modifies the list in place:
>>> numberlists = [[1,2,3],[4,5,6]]
>>> for numberlist in numberlists:
... numberlist.insert(0,9)
...
>>> numberlists
[[9, 1, 2, 3], [9, 4, 5, 6]]
or, more succintly
[numberlist.insert(0,9) for numberlist in numberlists]
or, differently, using list concatenation, which creates a new list
newnumberlists = [[9] + numberlist for numberlist in numberlists]
If you're going to be doing a lot of prepending,
perhaps consider using deques* instead of lists:
>>> mylist = [[1,2,3],[4,5,6],[7,8,9]]
>>> from collections import deque
>>> mydeque = deque()
>>> for li in mylist:
... mydeque.append(deque(li))
...
>>> mydeque
deque([deque([1, 2, 3]), deque([4, 5, 6]), deque([7, 8, 9])])
>>> for di in mydeque:
... di.appendleft(9)
...
>>> mydeque
deque([deque([9, 1, 2, 3]), deque([9, 4, 5, 6]), deque([9, 7, 8, 9])])
*Deques are a generalization of stacks and queues (the name is pronounced "deck" and is short for "double-ended queue"). Deques support thread-safe, memory-efficient appends and pops from either side of the deque with approximately the same O(1) performance in either direction.
And, as others have mercifully mentioned:
For the love of all things dull and ugly,
please do not name variables after your favorite data-structures.
#!/usr/bin/env python
def addNine(val):
val.insert(0,9)
return val
if __name__ == '__main__':
s = [[1,2,3],[4,5,6],[7,8,9]]
print map(addNine,s)
Output:
[[9, 1, 2, 3], [9, 4, 5, 6], [9, 7, 8, 9]]