Given two equal length lists:
a = [1,2,3,4,5,6,7,8,9]
b = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
Is there anyway to combine them, the first 3 (or more generally and for larger lists, the first n) elements of each at a time, such that I get an list output below:
c = [1,2,3,a,b,c,4,5,6,d,e,f,7,8,9,f,h,i]
I've seen solutions which show how to do it for 1 element at a time from each list but I can't translate that to n elements.
>>> list(itertools.chain.from_iterable(
a[p:p+n] + b[p:p+n] for p in range(0, len(a), n)))
[1, 2, 3, 'a', 'b', 'c', 4, 5, 6, 'd', 'e', 'f', 7, 8, 9, 'g', 'h', 'i']
Using list slicing
Ex:
a = [1,2,3,4,5,6,7,8,9]
b = ['a','b','c','d','e','f','g','h','i']
res = []
for i in range(0, len(a), 3):
res.extend(a[i:i+3] + b[i:i+3])
print(res)
Output:
[1, 2, 3, 'a', 'b', 'c', 4, 5, 6, 'd', 'e', 'f', 7, 8, 9, 'g', 'h', 'i']
Try list comprehension:
lst = [a[i:i+3] + b[i:i+3] for i in range(0, len(a), 3)]
result = [i for s in lst for i in s]
print (result)
Answer
[1, 2, 3, 'a', 'b', 'c', 4, 5, 6, 'd', 'e', 'f', 7, 8, 9, 'g', 'h', 'i']
Here is a functional efficient (fast) way without any mutation using the itertools grouper recipe. Another advantage is that it can be used with any number of lists, not just two.
from itertools import zip_longest, chain
a = [1,2,3,4,5,6,7,8,9]
b = 'a,b,c,d,e,f,g,h,i'.split(',')
def grouper(iterable, n, fillvalue = None):
"""Collect data into fixed-length chunks or blocks
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
"""
args = [iter(iterable)]*n
return zip_longest(*args, fillvalue=fillvalue)
def multi_round_robin(*iterables, n, fillvalue = None):
""" Collect data from multiple iterables in groups
# multi_round_robin('ABCD', 'EFGH', n=2) --> A B E F C D G H
"""
for tups in zip(*(grouper(itr, n, fillvalue) for itr in iterables)):
return chain.from_iterable(tups)
list(multi_round_robin(a, b, n=3))
# --> [1, 2, 3, 'a', 'b', 'c', 4, 5, 6, 'd', 'e', 'f', 7, 8, 9, 'g', 'h', 'i']
You can do this with simple for-loop and slices, where n - arrays length:
result = []
for j in range(0, len(a), n):
result += a[j:j+n] + b[j:j+n]
print(result)
I would use a function such as the following
def create_list(arr1, arr2, n):
c = []
for i in range(len(arr1)//n):
for j in range(3):
c.append(arr1[(i*3)+j])
for j in range(3):
c.append(arr2[(i*3)+j])
return c
This should give you the kind of list you were looking for.
Here's one way to do it:
In [14]: a
Out[14]: [1, 2, 3, 4, 5, 6, 7, 8, 9]
In [15]: b
Out[15]: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
In [17]: al = [a[i:i+3] for i in range(0, len(a), 3)]
In [18]: bl = [b[i:i+3] for i in range(0, len(b), 3)]
In [19]: al
Out[19]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
In [20]: bl
Out[20]: [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
In [23]: [k for i in list(zip(al, bl)) for j in i for k in j]
Out[23]: [1, 2, 3, 'a', 'b', 'c', 4, 5, 6, 'd', 'e', 'f', 7, 8, 9, 'g', 'h', 'i']
You can easily make a small function to perform this:
a = [1,2,3,4,5,6,7,8,9]
b = ['a','b','c','d','e','f','g','h','i']
def interleave( listA, listB, interval):
result = []
while len(listB) >= interval and len(listA) >= interval:
[result.append( listA.pop(0) ) for i in range(interval)]
[result.append( listB.pop(0) ) for i in range(interval)]
#Add remainder of lists in order
[result.append( item ) for item in listA]
[result.append( item ) for item in listB]
return result
print( interleave(a, b, 3) )
results in
[1, 2, 3, 'a', 'b', 'c', 4, 5, 6, 'd', 'e', 'f', 7, 8, 9, 'g', 'h', 'i']
Related
I have a list of lists in python in which I want to replace the first value of some lists with the first value of the list preceding it, but only for certain lists.
for i, x in enumerate(protected_rows):
k = protected_rows[i + 1]
dup_rows_indx = range(x + 2, k, 2) =
for j in dup_rows_indx:
try:
rows[j][0] = rows[j-1][0]
else:
continue
basically if my list was
rows = [[a, b, c], [d], [e, f, g, h, i], [j, k, l, m], [n], [o, p], [q, r, s], [t, u], [v, w, x]]
and protected_rows = [1, 4] I want the output to be
rows = [[a, b, c], [d], [e, f, g, h, i], [e, k, l, m], [n], [o, p], [o, r, s], [t, u], [t, w, x]]
Any help appreciated. Relatedly I also then want to remove rows[j-1] but I've completely failed at the first step. Thank you.
Just for clarification here my view on the sample you've provided:
rows = [
['a', 'b', 'c'],
['d'], # < protected row (index: 1)
['e', 'f', 'g', 'h', 'i'],
['j', 'k', 'l', 'm'],
['n'], # < protected row (index: 4)
['o', 'p'],
['q', 'r', 's'],
['t', 'u'],
['v', 'w', 'x']
]
My assumption is that [2, 5] should be [1, 4]. (If you don't want to adjust that then there's an easy fix.)
Now try
protected_rows = [1, 4]
indices = [-1] + protected_rows + [len(rows)]
for i_0, i_1 in zip(indices[:-1], indices[1:]):
for i in range(i_0 + 2, i_1, 2):
rows[i][0] = rows[i - 1][0]
or (if you want to stick with [2, 5])
protected_rows = [2, 5]
indices = [-1] + [i - 1 for i in protected_rows] + [len(rows)]
for i_0, i_1 in zip(indices[:-1], indices[1:]):
for i in range(i_0 + 2, i_1, 2):
rows[i][0] = rows[i - 1][0]
to get
rows = [
['a', 'b', 'c'],
['d'], # < protected row (index: 1)
['e', 'f', 'g', 'h', 'i'],
['e', 'k', 'l', 'm'], # < first item now 'e'
['n'], # < protected row (index: 4)
['o', 'p'],
['o', 'r', 's'], # < first item now 'o'
['t', 'u'],
['t', 'w', 'x'] # < first item now 't'
]
Is that what you are looking for?
Original Code:
The code is not reproducible, so I am making an example as I go. Suppose we have a list of lists of integers, and we want to replace the first element of each list with the first element of its predecessor, but only if the replacement candidate is an even number. Here's one way of doing this:
import copy
lst = [[1,2,3],[4,5,6],[7],[8,9,10],[11,12,13],[14,15]]
#create a copy to change.
lst2=copy.deepcopy(lst)
lst2
#iterate over both lists
for l,k in zip(lst, lst2[1:]):
#print (l,k)
if(k[0]%2==0):
k[0]=l[0]
print('====')
print(lst2)
# Out:
# [[1, 2, 3], [1, 5, 6], [7], [7, 9, 10], [11, 12, 13], [11, 15]]
Edit As per Comment- See Below
If, on the other hand, you want to filter alterable lists based on a list of protected indices, then it can be done simply like this :
import copy
lst = [[1,2,3],[4,5,6],[7],[8,9,10],[11,12,13],[14,15]]
protected_rows= [2,4]
#create a copy to change.
lst2=copy.deepcopy(lst)
#start counting from 1, include final index
for i in range(1,len(lst)):
if(i not in protected_rows ):
lst2[i][0]=lst2[i-1][0]
print(lst)
#print(protected_rows)
print(lst2)
#out:
# [[1, 2, 3], [4, 5, 6], [7], [8, 9, 10], [11, 12, 13], [14, 15]]
# [[1, 2, 3], [1, 5, 6], [7], [7, 9, 10], [11, 12, 13], [11, 15]]
Note: If we don't use a copy , the changes will cascade, i.e. the elements will be replaced by the altered value in previous list.
You can try this:
rows = [['a', 'b', 'c'], ['d'], ['e', 'f', 'g', 'h', 'i'], ['j', 'k', 'l', 'm'], ['n'], ['o', 'p'], ['q', 'r', 's'], ['t', 'u'], ['v', 'w', 'x']]
protected_rows = [2, 5]
for i in range(len(rows)):
for j in range(len(protected_rows)):
if i == protected_rows[j]:
rows[i+1][0] = rows[i][0]
for the rows[j-1] part please explain in more detail what you need.
output:
[['a', 'b', 'c'], ['d'], ['e', 'f', 'g', 'h', 'i'], ['e', 'k', 'l', 'm'], ['n'], ['o', 'p'], ['o', 'r', 's'], ['t', 'u'], ['v', 'w', 'x']]
I have a list of lists and I want to replace/update the third element of all the sublists with a new list.
lst1 = [['a','b','c', 3],['d','e','f', 9],['g','h','i', 'j']]
lst2 = [2, 3, 4]
Desired output:
lst_new = [['a','b', 2, 3],['d','e', 3, 9],['g','h', 4, 'j']]
Try this:
lst1 = [['a','b','c', 3],['d','e','f', 9],['g','h','i', 'j']]
lst2 = [2, 3, 4]
for x,y in zip(lst1,lst2): #loops over both lst1 and lst2
x[2] = y
output:
[['a', 'b', 2, 3], ['d', 'e', 3, 9], ['g', 'h', 4, 'j']]
Can you use numpy? As simple as:
arr1 = np.array(lst1)
arr1[:, 2] = lst2
Output:
array([['a', 'b', '2', '3'],
['d', 'e', '3', '9'],
['g', 'h', '4', 'j']], dtype='<U1')
lst1 = [['a','b','c', 3],['d','e','f', 9],['g','h','i', 'j']]
lst2 = [2,3,4]
#We make a copy so that this 'lst_new' variable does not point to lst1
lst_new = lst1.copy()
for num, lst in zip(lst2, lst_new):
lst[2] = num
#At this point, your lst_new will be changed
lst1 = [['a', 'b', 'c', 3], ['d', 'e', 'f', 9], ['g', 'h', 'i', 'j']]
lst2 = [2, 3, 4]
for x in range(0, len(lst2)):
lst1[x][2] = lst2[x]
print(lst1)
Just for the fun of having another option:
map(lambda e, subls: [*subls[:2], e, *subls[3:]], lst2, lst1))
It makes use of map, lambda and the unpacking operator *.
I have two lists with same length:
list_1 = [1,2,3,4,5,6]
list_2 = ['a','b','c','d','e','f']
I need to merge these lists based on n as below:
if n = 1: result = [1,'a',2,'b',3,'c',4,'d',5,'e',6,'f']
if n = 2: result = [1,2,'a','b',3,4,'c','d',5,6,'e','f']
if n = 3: result = [1,2,3,'a','b','c',4,5,6,'d','e','f']
if n = 4: result = [1,2,3,4,'a','b','c','d',5,6,'e','f'], and so on
Is there any pythonic way to achieve this?
So far I only know to do if n = 1 with list comprehension:
result = [x for sublist in zip(list_1, list_2) for x in sublist]
I don't know how to do it dynamically.
Try itertools(zip_longest and chain.from_iterable) with a list-comprehension, one liner:
import itertools
def merge(l1, l2, n):
return [j for i in zip(itertools.zip_longest(*[iter(l1)]*n), itertools.zip_longest(*[iter(l2)]*n)) for j in itertools.chain.from_iterable(i) if j]
list_1 = [1, 2, 3, 4, 5, 6]
list_2 = ["a", "b", "c", "d", "e", "f"]
print(merge(list_1, list_2, 2))
# [1, 2, 'a', 'b', 3, 4, 'c', 'd', 5, 6, 'e', 'f']
print(merge(list_1, list_2, 3))
# [1, 2, 3, 'a', 'b', 'c', 4, 5, 6, 'd', 'e', 'f']
print(merge(list_1, list_2, 4))
# [1, 2, 3, 4, 'a', 'b', 'c', 'd', 5, 6, 'e', 'f']
Some possible reference:How does zip(*[iter(s)]*n) work in Python?
Alternative answer using generators:
list_1 = [1,2,3,4,5,6]
list_2 = ['a','b','c','d','e','f']
def merge(a, b, n):
a_index = 0
b_index = 0
while(a_index < len(a)):
for _ in range(n):
yield a[a_index]
a_index +=1
for _ in range(n):
yield b[b_index]
b_index += 1
result = [x for x in merge(list_1, list_2, 1)]
assert result == [1, 'a', 2, 'b', 3, 'c', 4, 'd', 5, 'e', 6, 'f']
result = [x for x in merge(list_1, list_2, 2)]
assert result == [1, 2, 'a', 'b', 3, 4, 'c', 'd', 5, 6, 'e', 'f']
result = [x for x in merge(list_1, list_2, 3)]
assert result == [1,2,3,'a','b','c',4,5,6,'d','e','f']
Only works for lists with the same size and probably has some more pitfalls.
Edit: just for fun, here's a version without managing an index.
def merge(a, b, n):
gen_a = (x for x in a)
gen_b = (x for x in b)
try:
while True:
for _ in range(n):
yield next(gen_a)
for _ in range(n):
yield next(gen_b)
except StopIteration:
pass
def main(order, iArr, sArr):
arr = []
for type in order:
if type == 'i':
arr.append(iArr[0])
iArr.remove(iArr[0])
else:
arr.append(sArr[0])
sArr.remove(sArr[0])
return arr
order1 = ['i', 's', 'i', 's', 'i', 's', 'i', 's', 'i', 's', 'i', 's']
order2 = ['i', 'i', 's', 's', 'i', 'i', 's', 's', 'i', 'i', 's', 's']
order3 = ['i', 'i', 'i', 's', 's', 's', 'i', 'i', 'i', 's', 's', 's']
list_1 = [1,2,3,4,5,6]
list_2 = ['a', 'b', 'c', 'd', 'e', 'f']
print(main(order2, list_1, list_2))
I am trying to create all combinations of two sets of lists using as follows:
x = [[1,2,3],[4,5,6]]
y = [['a','b','c'],['d','e','f']]
combos = [[1,2,3,'a','b','c'],[4,5,6,'d','e','f'],[4,5,6,'a','b','c'],[4,5,6,'d','e','f']]
I think itertools may be of some help but not sure how. Thanks
You can use product and chain:
from itertools import product, chain
[list(chain(*i)) for i in product(x, y)]
#[[1, 2, 3, 'a', 'b', 'c'],
# [1, 2, 3, 'd', 'e', 'f'],
# [4, 5, 6, 'a', 'b', 'c'],
# [4, 5, 6, 'd', 'e', 'f']]
Or you can use a list comprehension:
[i + j for i in x for j in y]
#[[1, 2, 3, 'a', 'b', 'c'],
# [1, 2, 3, 'd', 'e', 'f'],
# [4, 5, 6, 'a', 'b', 'c'],
# [4, 5, 6, 'd', 'e', 'f']]
I have an example multidimensional list:
example_list=[
["a","b","c", 2,4,5,7],
["e","f","g",0,0,1,5],
["e","f","g", 1,4,5,7],
["a","b","c", 3,2,5,7]
]
How is it possible to put them in groups like this:
out_list=[
[["a","b","c", 2,4,5,7],
["a","b","c",3,2,5,7]
],
[["e","f","g", 0,0,1,5],
["e","f","g", 1,4,5,7]
]
]
I have tried this:
example_list=[["a","b","c", 2,4,5,7],["e","f","g", 0,0,1,5],["e","f","g",1,4,5,7],["a","b","c", 3,2,5,7]]
unique=[]
index=0
for i in range(len(example_list)):
newlist=[]
if example_list[i][:3]==example_list[index][:3]:
newlist.append(example_list[i])
index=index+1
unique.append(newlist)
print unique
My results is this:
[[['a', 'b', 'c', 2, 4, 5, 7]], [['e', 'f', 'g',0, 0, 1, 5]], [['e', 'f', 'g', 1, 4, 5, 7]], [['a', 'b', 'c', 3, 2, 5,7]]]
I could not figure it out.
If the grouping is decided by the first three elements in each list following code will do what you're asking for:
from collections import defaultdict
example_list=[["a","b","c", 2,4,5,7],["e","f","g",0,0,1,5],["e","f","g", 1,4,5,7],["a","b","c", 3,2,5,7]]
d = defaultdict(list)
for l in example_list:
d[tuple(l[:3])].append(l)
print d.values() # [[['a', 'b', 'c', 2, 4, 5, 7], ['a', 'b', 'c', 3, 2, 5, 7]], ...]
This will use defaultdict to generate a dictionary where keys are the first three elements and values are list of lists which start with those elements.
First sort the list simply using sorted(), providing a lambda function as key.
>>> a = sorted(example_list, key=lambda x:x[:3])
[['a', 'b', 'c', 2, 4, 5, 7], ['a', 'b', 'c', 3, 2, 5, 7], ['e', 'f', 'g', 0, 0, 1, 5], ['e', 'f', 'g', 1, 4, 5, 7]]
And then use itertools.groupby() on the sorted list:
>>> [list(v) for k, v in groupby(a, lambda x:x[:3])]
[
[['a', 'b', 'c', 2, 4, 5, 7], ['a', 'b', 'c', 3, 2, 5, 7]],
[['e', 'f', 'g', 0, 0, 1, 5], ['e', 'f', 'g', 1, 4, 5, 7]]
]