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))
Related
given these sublists
lst=[['a', 'b', 'c', 'd', 'e'], ['f', 'g', 'h']]
I am trying to find the location of its elements,
for instance, the letter 'a' is located at 0,0
but this line
print(lst.index('a'))
instead produces the following error:
ValueError: 'a' is not in list
if your list have depth=2, you can use this:
lst=[['a', 'b', 'c', 'd', 'e'], ['f', 'g', 'h']]
def fnd_idx(char, lst):
for x in range(len(lst)):
try:
idx = lst[x].index(char)
return [x,idx]
except ValueError:
pass
return None
Output:
>>> print(fnd_idx('a', lst))
[0, 0]
>>> print(fnd_idx('g', lst))
[1, 1]
>>> print(fnd_idx('z', lst))
None
You can use list comprehension:
>>> lst=[['a', 'b', 'a', 'd', 'a'], ['f', 'g', 'a'], ['a','a','b']]
>>> [(i,j) for i in range(len(lst)) for j in range(len(lst[i])) if lst[i][j]=='a']
[(0, 0), (0, 2), (0, 4), (1, 2), (2, 0), (2, 1)]
Try this function (just one line of code!):
def idx(lst, el):
return next(((i, sublst.index(el))
for i, sublst in enumerate(lst)
if el in sublst),
None)
So for example:
>>> idx(lst, 'a')
(0, 0)
>>> idx(lst, 'c')
(0, 2)
lst=[['a', 'b', 'c', 'd', 'c'], ['f', 'g', 'h']]
searchvalue = 'f'
counter = 0
for index in lst:
if searchvalue in index:
print(counter, index.index(searchvalue))
counter+=1
If 'a' can appear in multiple sublists, and you want the index in each sublist:
def GetIndexes(lst, val):
pos = []
for sublist in lst:
try:
idx = sublist.index(val)
pos.append(idx)
except:
pos.append(None)
return pos
In your example : [0, None]
Meaning: In sublist 0, the first 'a' is at the index 0. In sublist 1, there is no 'a'.
you can do this with numpy, the benefit is you don't have to hardcode anything for the size of nested lists. you can have hundreds or 3 and this will work!
lst=[['a', 'b', 'c', 'd', 'e'], ['f', 'g', 'h']]
arr = np.array(lst, dtype=object)
for x in arr:
try:
print (x.index('a'), x)
except:
pass
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 *.
How can I create a list recursively?
I have this list:
l = ['a', 'b', 'new', 'c', 'd', 'new', 'z', 'x', 'c', 'fin', 'f', 'fin',
'g', 'l', 'new', 'z', 'x', 'c', 'fin', 'j']
The expected output is:
r = ['a', 'b', ['c', 'd', ['z', 'x', 'c'] 'f'], 'g', 'l', ['z', 'x', 'c'] 'j']
What I have tried so far:
def asd(l, index=0):
r = []
for i in l[index:]:
index += 1
if i == 'new':
i, index = asd(l, index)
r.append(i)
if i == 'fin':
return r
return r, index
r, index = asd(l)
I cannot understand how to make it work. Can anyone help me?
This is a non-recursive solution that can create your list, parsing in one pass without any need for costly index() operations:
l = ['a', 'b', 'new', 'c', 'd', 'new', 'f', 'fin', 'g', 'fin', 'j']
rv = []
curr = [rv] # things are always added to the last element if not 'fin' or 'new'
for elem in l:
if elem == "new":
# create a new list, put it at end of curr
curr.append([])
# add that list to the one before
curr[-2].append(curr[-1])
elif elem == "fin":
# done, remove from curr
curr.pop()
else:
curr[-1].append(elem)
print(rv)
Output:
['a', 'b', ['c', 'd', ['f'], 'g'], 'j']
l = ['a', 'b', 'new', '1', '2', '3', 'fin', 'c', 'new', 'x', 'y', 'z', 'fin',]
leads to
['a', 'b', ['1', '2', '3'], 'c', ['x', 'y', 'z']]
You need to foolproof it against unbalanced / incorrect new/fin's
Edited to make it more concise after Matthieu's comment.
Here is a straight forward recursive solution, using a deque as a stack data structure from which you can popleft the leftmost element in O(1).
Algorithm
from collections import deque
def nest(lst):
return _nest(deque(lst))
def _nest(deq):
result = []
while deq:
x = deq.popleft()
if x == 'fin':
break
elif x == 'new':
result.append(_nest(deq))
else:
result.append(x)
return result
Tests
tests = [
[],
[1, 2, 3],
[1, 2, 'new', 3, 4, 'fin', 5],
[1, 2, 'new', 3, 4, 'fin', 5, 6, 'new', 7, 'fin'],
['new', 'fin', 'new', 'fin', 'new', 'new', 'fin', 'fin'],
['new', 1, 2, 'fin'],
[1, 2, 3, 'new', 4, 'new', 5, 6, 'fin', 7, 8, 'fin', 9, 10, 'new', 11, 'fin', 12, 13]
]
for test in tests:
print(nest(test))
Output
[]
[1, 2, 3]
[1, 2, [3, 4], 5]
[1, 2, [3, 4], 5, 6, [7]]
[[], [], [[]]]
[[1, 2]]
[1, 2, 3, [4, [5, 6], 7, 8], 9, 10, [11], 12, 13]
You can use a stack instead and go through the list and using it:
def parse(l):
stack = [[]]
for i in l:
if i == "new":
stack.append([])
elif i == "fin":
pop = stack.pop()
stack[-1].append(pop)
else:
stack[-1].append(i)
return stack[0]
Recursive alternative :
def asd(l):
if 'new' in l:
index_new = l.index('new')
keyword = 1
for index_fin,e in enumerate(l[index_new+1:], index_new+1):
if e == 'new':
keyword += 1
elif e == 'fin':
keyword -=1
if not keyword:
break
return l[:index_new] + [asd(l[index_new+1:index_fin])] + asd(l[index_fin+1:])
else:
return l
Input :
['a', 'b', 'new', 'c', 'd', 'new', 'z', 'x', 'c', 'fin', 'f', 'fin',
'g', 'l', 'new', 'z', 'x', 'c', 'fin', 'j']
Output :
['a', 'b', ['c', 'd', ['z', 'x', 'c'], 'f'], 'g', 'l', ['z', 'x', 'c'], 'j']
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']
I have a list of lists like this:-
x=[['A','B','C','D'],['E','F','G','H']]
I am trying to add an index to the list like this:-
y=[[0,'A','B','C','D'],[1,'E','F','G','H']]
Is there any way to achieve this?
y = [[i]+a for i,a in enumerate(x)]
Use enumerate and insert:
x = [['A','B','C','D'],['E','F','G','H']]
y = []
for index, li in enumerate(x):
li_copy = li[:]
li_copy.insert(0, index)
y.append(li_copy)
print(y)
# [[0, 'A', 'B', 'C', 'D'], [1, 'E', 'F', 'G', 'H']]
Or if you don't mind overwriting x:
x = [['A','B','C','D'],['E','F','G','H']]
for index, li in enumerate(x):
li.insert(0, index)
print(x)
# [[0, 'A', 'B', 'C', 'D'], [1, 'E', 'F', 'G', 'H']]
if you are looking for a simple function that achieves this try the following:
def add_index(itemList):
i = 0
setOfLists = []
for x in itemList:
set_of_lists.append([i] + x)
i +=1
return setOfLists
Could also use collections.deqeue for this:
from collections import deque
lst = [['A','B','C','D'],['E','F','G','H']]
result = []
for i, l in enumerate(lst):
q = deque(l)
q.appendleft(i)
result.append(list(q))
print(result)
Which Outputs:
[[0, 'A', 'B', 'C', 'D'], [1, 'E', 'F', 'G', 'H']]