Insert multiple elements into a specific indexes [duplicate] - python

This question already has answers here:
How to insert multiple values by index into list at one time
(4 answers)
Closed 7 months ago.
I want to solve the following problem in one line. I looked at the itertools docs to find a specific function for this but no luck. Say
a = [1, 2, 3,4]
b = [5, 6, 7, 8]
I want to insert the elements of b into a, but each element into a specific index. So
insert_function(a, b, keys=[0,1, 2, 3])
should return
[5, 1, 6, 2, 7, 3, 8, 4]

One approach:
def insert_function(la, lb, keys=None):
ii = [i + k for i, k in enumerate(keys)]
i, j = 0, 0
ret = []
for r in range(len(la) + len(lb)):
if r in ii:
ret.append(lb[j])
j += 1
else:
ret.append(la[i])
i += 1
return ret
res = insert_function(a, b, keys=[0, 1, 2, 3])
print(res)
Output
[5, 1, 6, 2, 7, 3, 8, 4]
Or as an alternative use this one-liner list comprehension with O(n + m) time complexity:
def insert_function(la, lb, keys=None):
ii = set(i + k for i, k in enumerate(keys))
it_a, it_b = iter(a), iter(b)
return [next(it_a) if r not in ii else next(it_b) for r in range(len(la) + len(lb))]
res = insert_function(a, b, keys=[0, 1, 2, 3])
print(res)

The problem is that when you insert elements you shift the subsequent indices. That can be prevented by inserting from right to left:
def insert_function(a, b, keys):
# copy so original list is left intact
a = a.copy()
# sort the keys in reverse
for k,v in sorted(zip(keys, b), key=lambda x:x[0], reverse=True):
a.insert(k, v)
return a
# returns [5, 1, 6, 2, 7, 3, 8, 4]
print(insert_function(a, b, [0, 1, 2, 3]))

This should work!
def insert_function(a, b, keys):
assert len(b) == len(keys)
for i in range(len(b)):
a.insert(keys[i], b[i])

Related

How to shift items in an array by a "K" number of times?

Shift the items in the given array, by some number of times, as shown in the below examples;
array = [1, 2 ,3 , 4, 5, 6]
k1 = 2
k2 = -3
k3 = 20
test1:
cirShift(array, k1)
Result: [5, 6, 1, 2, 3, 4]
test2:
cirShift(array, k2)
Result: [4, 5, 6, 1, 2, 3]
test3:
cirShift(array, k3)
Result: [5, 6, 1, 2, 3, 4]
I have used the below to achieve the right-rotate a list by k positions;
def rightRotateByOne(A):
Fin= A[-1]
for i in reversed(range(len(A) - 1)):
A[i + 1] = A[i]
A[0] = Fin
def rightRotate(A, k):
if k < 0 or k >= len(A):
return
for i in range(k):
rightRotateByOne(A)
if __name__ == '__main__':
A = [1, 2, 3, 4, 5, 6, 7]
k = 3
rightRotate(A, k)
print(A)
As of now, able to obtain results for test1 but would like to achieve the test2 and test3
Even easier, split the array in half given the boundary, swap and glue back:
def cirShift(a, shift):
if not shift or not a:
return a
return a[-shift%len(a):] + a[:-shift%len(a)]
Courtesy of #KellyBundy, a short-circut one-liner:
def cirShift(a, shift):
return a and a[-shift%len(a):] + a[:-shift%len(a)]
I think this question may be an exercise in self learning ('how to do X using just python'), so my answer is auxiliary, but you can always use np.roll():
#test 1
import numpy as np
a = [1, 2 ,3, 4, 5, 6]
np.roll(a, 2)
gives output
[5, 6, 1, 2, 3, 4]
and
#test 2
np.roll(a, -2)
gives output
[3, 4, 5, 6, 1, 2]
Even if you give a number that is larger than the array size, it handles the overflow:
#test 3
np.roll(a, 10)
gives output
[3, 4, 5, 6, 1, 2]
Rolling also works in multiple dimension arrays and across specified axes, which is pretty neat.
def shift(l, shift_t):
r = [*l]
for n in range(abs(shift_t)):
if shift_t < 0:
r = r[1:] + [r[0]]
else:
r = [r[-1]] + r[:-1]
return r
The key is to take one item of the list and place it on the opposite side, which is essentially all that shifting is doing. If you shift negative, you put the first one at the end, and if you shift positive, you put the last one at the beginning.

I am getting error when running this loop

You are given an array of integers a. A new array b is generated by rearranging the elements of a in the following way:
b = [a[0], a[len(a)-1], a[1], a[len(a)-2, ...]
my code only loops for one time, and I am just stuck from here. What I have tried is below
def alternatingSort(a):
length = len(a)
b = []
for i in range(length):
if i % 2:
b.append(a[length-i])
else:
b.append(a[i])
return b
if my input is [1, 3, 5, 6, 4, 2], my output should be [1,2,3,4,5,6]
But i get [1, 2, 5, 6, 4, 3].
Your logic is not correct. Here, is the working solution with minimum changes:
def alternatingSort(a):
length = len(a)
b = []
for i in range(length):
if i % 2:
b.append(a[length - (i // 2) - 1]) # Updated.
else:
b.append(a[i // 2]) # Updated.
return b
a = [1, 3, 5, 6, 4, 2]
print(alternatingSort(a))
a = [1, 3 ,2]
print(alternatingSort(a))

Filtering indexes of list

I have the following list of numbers:
List = [1, 2, 3, 4, 5, 6, 15]
I want the indexes of those numbers which are multiple of n, so I do:
def indexes(List, n):
# to enumerate the numbers
E = enumerate(List)
# filtering tuples
F = list(filter(lambda x: x[1] % n == 0, E))
return [ i[0] for i in F]
indexes(List, 2)
[1, 3, 5]
That's ok, but what happens when I add the variable m?
def Index( L, n, m):
# enumeration
E = enumerate(L)
# filtering tuples
F_n = list(filter(lambda x: x[1]%n == 0, E))
F_m = list(filter(lambda x: x[1]%m == 0, E))
L_n = [ l[0] for l in F_n]
L_m = [ J[0] for J in F_m]
return L_n + L_m
>>>Index(List, 2, 5):
[1, 3, 5]
Why that code doesn't returns [1, 3, 5, 4, 6]?
What is the mistake?
And how to create the function that returns that list?
You can use a list comprehension in combination with enumerate method.
Also, you can apply extended iterable unpacking operator in order to pass parameters as many you need.
List = [1, 2, 3, 4, 5, 6, 15]
def indexes(List, *vars):
return [index for index, item in enumerate(List) for i in vars if item % i == 0 ]
print(indexes(List, 2, 5))
Output
[1, 3, 5, 4, 6]
A more general and Pythonic approach that works for any number of variables is to use an any() or all() function that check the Truth value of the condition for all the arguments. If you want the index to belongs to an item that is divisible buy all the arguments you need all() other wise you can use any() that returns True right after it encounters a match.
def indexes(lst, *args):
return [i for i, j in enumerate(lst) if any(j % arg == 0 for arg in args)]
Demo:
>>> lst = [1, 2, 3, 4, 5, 6, 15, 99, 200, 13, 17, 400]
>>> indexes(lst, 99, 5, 2, 100)
[1, 3, 4, 5, 6, 7, 8, 11]
>>>
And with all():
>>> indexes(lst, 5, 2, 100)
[8, 11]
The issue is enumerate returns an iterator from an iterable. Once it is exhausted, you may not use it again. Therefore, you can simply define a new enumerate iterator:
lst = [1, 2, 3, 4, 5, 6, 15]
def Index( L, n, m):
# enumeration - notice we define 2 objects
E, F = enumerate(L), enumerate(L)
F_n = list(filter(lambda x: x[1]%n == 0, E))
F_m = list(filter(lambda x: x[1]%m == 0, F))
L_n = [ l[0] for l in F_n]
L_m = [ J[0] for J in F_m]
return L_n + L_m
res = Index(lst, 2, 5)
print(res)
[1, 3, 5, 4, 6]
Note there are better ways you can implement your algorithm.

Combining array elements in a particular way, and recording it

I am given a 1D array of numbers.
I need to go through the array adding each consecutive element to form a sum. Once this sum reaches a certain value, it forms the first element of a new array. The sum is then reset and the process repeats, thus iterating over the whole array.
For example if given:
[1, 3, 4, 5, 2, 5, 3]
and requiring the minimum sum to be 5,
the new array would be:
[8, 5, 7]
Explicity: [1 + 3 + 4, 5, 2 + 5]
I then also need to keep a record of the way the elements were combined for that particular array: I need to be to take a different array of the same length and combine the elements in the same way as above.
e.g. give the array
[1, 2, 1, 1, 3, 2, 1]
I require the output
[4, 1, 5]
Explicity: [1 + 2 + 1, 1, 3 + 2]
I have accomplished this with i loops and increment counters, but it is very ugly. The array named "record" contains the number of old elements summed to make each element of the new array i.e. [3, 1, 2]
import numpy as np
def bin(array, min_sum):
num_points = len(array)
# Create empty output.
output = list()
record = list()
i = 0
while i < num_points:
sum = 0
j = 0
while sum < min_sum:
# Break out if it reaches end of data whilst in loop.
if i+j == num_points:
break
sum += array[i+j]
j += 1
output.append(sum)
record.append(j)
i += j
# The final data point does not reach the min sum.
del output[-1]
return output
if __name__ == "__main__":
array = [1, 3, 4, 5, 2, 5, 3]
print bin(array, 5)
I would advice you to simply walk through the list. Add it to an accumulator like the_sum (do not use sum, since it is a builtin), and in case the_sum reaches a number higher than the min_sum, you add it, and reset the_sum to zero. Like:
def bin(array, min_sum):
result = []
the_sum = 0
for elem in array:
the_sum += elem
if the_sum >= min_sum:
result.append(the_sum)
the_sum = 0
return result
The lines where the accumulator is involved, are put in boldface.
I leave combining the other array the same way as an exercise, but as a hint: use an additional accumulator and zip to iterate over both arrays concurrently.
Here is a straightforward solution. which computes a list of boolean values where the value is true when accumulated element equals or exceeds the target value and calc computes an accumulation using this list.
def which(l, s):
w, a = [], 0
for e in l:
a += e
c = (a >= s)
w.append(c)
if c:
a = 0
return w
def calc(l, w):
a = 0
for (e, c) in zip(l, w):
a += e
if c:
yield a
a = 0
here is an interactive demonstration
>>> l1 = [1, 3, 4, 5, 2, 5, 3]
>>> w = which(l1, 5)
>>> w
[False, False, True, True, False, True, False]
>>> list(calc(l1, w))
[8, 5, 7]
>>> l2 = [1, 2, 1, 1, 3, 2, 1]
>>> list(calc(l2, w))
[4, 1, 5]
You can use short solutions I found out after a long struggle with flattening arrays.
For getting bounded sums use:
f = lambda a,x,j,l: 0 if j>=l else [a[i] for i in range(j,l) if sum(a[j:i])<x]
This outputs:
>>> f = lambda a,x,j,l: 0 if j>=l else [a[i] for i in range(j,l) if sum(a[j:i])< x]
>>> a= [1, 3, 4, 5, 2, 5, 3]
>>> f(a,5,0,7)
[1, 3, 4]
>>> sum(f(a,5,0,7))
8
>>> sum(f(a,5,3,7))
5
>>> sum(f(a,5,4,7))
7
>>>
To get your records use the function:
>>> y = lambda a,x,f,j,l: [] if j>=l else list(np.append(j,np.array(y(a,x,f,j+len(f(a,x,j,l)),l))))
From here, you can get both array of records and sums:
>>> listt=y(a,5,f,0,len(a))
>>> listt
[0.0, 3.0, 4.0, 6.0]
>>> [sum(f(a,5,int(listt[u]),len(a))) for u in range(0,len(listt)-1)]
[8, 5, 7]
>>>
Now, the bit of magic you can even use it as an index-conditional boundary for the second vector:
>>> b=[1, 2, 1, 1, 3, 2, 1]
>>> [sum(f(b,5,int(listt[u]),int(listt[u+1]))) for u in range(0,len(listt)-1)]
[4, 1, 5]
>>>

Python - How to extract numbers in list A depending on list B?

My purpose is:
For every element in B, I want to create a list, in which it contains elements of list B and smaller elements(than B element) in list A.
I tried two for loops, however, I do not know how to finish this work :
A=[1,2,3,4,5,6,7,8,9]
B=[3,4,5]
C=[]
for i in B:
for r in A:
if i>=r:
C.append(r)
I expect a result like this:
[[3,1,2,3],[4,1,2,3,4],[5,1,2,3,4,5]]
Any suggestions?
Try this:
for i in B:
new_list = [i] # inner list starting with the elmt from B
for r in A:
if i >= r:
new_list.append(r) # append to inner list
C.append(new_list) # finally append inner list to C
Have you tried using an array inside one of the loops:
A=[1,2,3,4,5,6,7,8,9]
B=[3,4,5]
C=[]
for b in B:
c = [b]
for a in A:
if a <= b:
c.append(a)
C.append(c)
print(C)
If you wanted to write this in one line:
A=[1,2,3,4,5,6,7,8,9]
B=[3,4,5]
C=[[b] + [a for a in A if a <= b] for b in B]
print(C)
prints
[[3, 1, 2, 3], [4, 1, 2, 3, 4], [5, 1, 2, 3, 4, 5]]
One solution could be following :
try:
A = [1, 2, 3, 4, 5, 6, 7, 8, 9]
B = [3, 4, 5]
D = []
a = sorted(A)
for b in B:
temp = [b]
temp.extend(a[0: a.index(b) + 1])
D.append(temp)
print(D)
except ValueError as e:
pass # Do something when value in B not found in a
You could use filter and list comprehension for one line operation
print [[i]+filter(lambda x: x <= i, A) for i in B]
results in
[[3, 1, 2, 3], [4, 1, 2, 3, 4], [5, 1, 2, 3, 4, 5]]
Here is your code
A=[1,2,3,4,5,6,7,8,9]
B=[3,4,5]
res=list()
for i in B:
C=list()
C.append(i)
for r in range(0,i):
C.append(A[r])
res.append(C)
print res,

Categories

Resources