Remove all occurences of a given value in multiple arrays at once - python

I have four arrays, say, A, B, C and D, of the same size NumElements, and I want to remove all the 0s in them. If A has a zero, B, C and D have one too, in the same position. So I was thinking to loop over the elements of A:
for n in range(NumElements):
if A[n]==0:
A.pop(n)
B.pop(n)
C.pop(n)
D.pop(n)
Of course, this doesn't work, because popping 0s from the arrays reduces their sizes, so I end up trying to access A[NumElements-1], when now A is only NumElements-m long. I know I should work with array copies, but the arrays are quite long and I'd like to keep memory consumption low, since I'm working in a Java virtual machine (don't ask :(((( ). Also, I'd like an approach which is efficient, but most of all readable (this code must be maintained by Python illiterates like me, so I need to KISS).

a,b,c,d = [filter(lambda i: i != 0, l) for l in [a,b,c,d]]
Filter each list removing elements that are not 0.
Edit,
Just to explain whats happening
Filter takes an expression and "filters" the list, by applying the function to everything in the list, everything that does not return True.
Lambda is a short hand for a function
So
a = [1,2,3,4,5,6,7,8]
def is_even(x):
return x % 2 == 0
filter(is_even, a)

If they all have zeros in the same place, then loop over the index in reverse and remove that index from each list:
for i in reversed(range(NumElements)):
if not A[i]:
del A[i], B[i], C[i], D[i]
By looping over the list in reverse, you keep the indices stable (only elements past the current index have been removed, shrinking only the tail of the lists). Since you are not using the return value of list.pop() (all you get is 0s anyway, right?), you may as well just use del on the list index instead.
I used reversed(range(NumElements)) here instead of calculating the more strenuous range(NumElements - 1, -1, -1); it is just as efficient but a lot more readable. The reversed() function returns an iterator, handling the reversed number sequence very efficiently. On Python 2, you can do the same with xrange():
for i in reversed(xrange(NumElements)):
Demo:
>>> A = [1, 2, 0, 4, 5, 0]
>>> B = [2, 4, 0, 10, 9, 0]
>>> C = [5, 3, 0, 10, 8, 0]
>>> D = [10, 3, 0, 1, 34, 0]
>>> for i in reversed(range(NumElements)):
... if not A[i]:
... del A[i], B[i], C[i], D[i]
...
>>> A, B, C, D
([1, 2, 4, 5], [2, 4, 10, 9], [5, 3, 10, 8], [10, 3, 1, 34])

Just work from the other end!
for n in range(NumElements-1,-1,-1):
if A[n]==0:
A.pop(n)
B.pop(n)
C.pop(n)
D.pop(n)

I think you could do smth like this. I don't know if it's pythonic enough.
A = [1, 2, 4, 0]
B = [6, 0, 4, 3, 9]
C = [12, 5, 32, 0, 90]
for row in [A, B, C]:
for i, v in enumerate(row):
if v == 0: del row[i]
or, if you sure that indexes of zero are equal in all lists:
for i in range(len(A) - 1, -1, -1):
if A[i] == 0:
for row in [A, B, C]:
del row[i]

Look at my other answer List accessing in Python. You can walk through list A and store indexes of 0s in temporary list and then pop them.

This is probably a hack but it's simple and it works
>>> a = [1,2,3]
>>> b = [1,10,99]
>>> c = [1,87,22]
>>> d = []
>>> d.extend([a,b,c])
>>> to_remove = 1
>>> [i.remove(to_remove) for i in d]
>>> d
[[2, 3], [10, 99], [87, 22]]
Note that this will remove all elements marked as to_remove not just zeros at the beginning, I'm assuming this is ok for you because you say that you want to remove all the zeros.

Related

what is the issue with my code for Rearrange an array such that arr[i] = i?

I want to Rearrange an array such that each element located in its index. Or in result of program if element exist in that array: Output_array[element] = element located in its location otherwise Output_array[element] = -1.
For example if input array be :
a= [-1,-1,6,3,5,9,1,0,8,2]
then output is:
output_array= [0,1,2,3,-1,5,-1,-1,8,9]
as you see, if we ignore -1 in this array: then output_array is sorted.
this is a Python 3 code. it is printing some elements correctly, but not all
def solve(a):
n = len(a)
for i in range (n):
if i in a:
a[i] = i
else:
a[i] = -1
print(a)
a= [-1,-1,6,3,5,9,1,0,8,2]
solve(a)
Problems:
The problem is element at an index is replaced with -1 if it's not present.
When the code tries to check 5, 5 is already replaced by -1 in the previous step (a[4] = -1 (since 4 is not in the list), which is replacing 5). Similarly for the rest of the anomaly cases.
To better understand what's happening, add some prints:
def solve(a):
n = len(a)
for i in range(n):
if i in a:
a[i] = i
print(a)
print(i)
else:
a[i] = -1
print(a)
Solution:
One solution would be to use a list comprehension which does this in one line and is pretty faster:
[i if i in a else -1 for i in range(len(a))]
In function:
def solve(a):
return [i if i in a else -1 for i in range(len(a))]
And call it as:
>>> solve([-1,-1,6,3,5,9,1,0,8,2])
[0, 1, 2, 3, -1, 5, 6, -1, 8, 9]
Considering your trying to achieve an array with elements arranged into their respective index value.
The thing happening here is since the list is a mutable data type your data is being overwritten with every iteration and due to this not all values are being arranged.
for example your list after loop running from i=0 to i=3 becomes
[0,1,2,3,5,9,1,0,8,2]
You see you have lost some of your data like 6 due to changes in list,try using different list to store changes!!
The point is that when i reaches 6, 6 is no more in a, because a[2] was set to -1 when i was 2 ! You should first make a copy of a into b, and your condition should be if i in b instead of if i in a
I used another list, you could use another array, not matter.
def solve(a):
n = len(a)
c=[]
for i in range (n):
if i in a:
c.append(i)
else:
c.append(-1)
print(a)
print(c)
a= [-1,-1,6,3,5,9,1,0,8,2]
solve(a)
And output :
[-1, -1, 6, 3, 5, 9, 1, 0, 8, 2]
[0, 1, 2, 3, -1, 5, 6, -1, 8, 9]
What was issue with your code?
In this part of your code: you changed element in forward index i and be changing them, you lost them and not able to find in the remaining index of for loop.
if i in a:
a[i] = i
else:
a[i] = -1
def solve(a):
return [i if i in a else -1 for i in range(len(a))]
# Test
a = [3, 5, 0, 2, 3, 4]
print(solve(a))
Result:
[0, -1, 2, 3, 4, 5]

Loop from a specific point in a list of lists Python

I would like to append to a new list all elements of an existing list of lists after a specific point
m = [[1,2,3],[4,5,10],[6,2,1]]
specific point = m[0][2]
newlist = [3,4,5,10,6,2,1]
You can directly slice off the remainder of the first target list and then add on all subsequent elements, eg:
m = [[1,2,3],[4,5,10],[6,2,1]]
y, x = 0, 2
new_list = m[y][x:] + [v for el in m[y+1:] for v in el]
# [3, 4, 5, 10, 6, 2, 1]
Here's a couple of functional approaches for efficiently iterating over your data.
If sublists are evenly sized, and you know the index from where to begin extracting elements, use chain + islice:
from itertools import chain, islice
n = 3 # Sublist size.
i,j = 0,2
newlist = list(islice(chain.from_iterable(m), i*n + j, None))
If you don't know the size of your sublists in advance, you can use next to discard the first portion of your data.
V = chain.from_iterable(m)
next(v for v in V if v == m[i][j])
newlist = list(V)
newlist.insert(m[i][j], 0)
This assumes there is no identical value earlier in the sequence.
You can put a conditional in your iteration and only add based on that condition. Once you hit that specific index, make your condition true. Something like this:
m = [[1,2,3],[4,5,10],[6,2,1]]
specific_point = (0,2)
newlist = [3,4,5,10,6,2,1]
output = []
for i in range(len(m)):
for j in range(len(m[i])):
if (i,j) < specific_point:
continue
output.append(m[i][j])
output:
[3, 4, 5, 10, 6, 2, 1]
why not flatten the initial list and go from there
flat_list = [item for sublist in m for item in sublist]
would return [1,2,3,4,5,10,6,2,1] so now you're really on flat_list[2:]
Most of the answers only work for this specific shape of nested list, but it's also possible to create a solution that works with any shape of nested list.
def flatten_from(sequence, path=[]):
start = path.pop(0) if path else 0
for item in sequence[start:]:
if isinstance(item, (list, tuple)):
yield from flatten_from(item, path)
else:
yield item
With the example from the question
>>> list(flatten_from([[1, 2, 3], [4, 5, 10], [6, 2, 1]], [0, 2]))
[3, 4, 5, 10, 6, 2, 1]
It also works with any shape and level of nesting of the input data
m = [[1], [[2], [3, 4, 5, 6, 7]], 8, [9, [10, 11]]]
flatten_from(m, [])) # 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
flatten_from(m, [2]) # 8, 9, 10, 11
flatten_from(m, [1, 1, 3]) # 6, 7, 8, 9, 10, 11
This is a bit of a bastard algorithm, though. On one hand, it uses nice functional programming concepts: recursion and yield.
On the other hand it relies on the side effect of mutating the path argument with list.pop, so it's not a pure function.
Below solution will work for your case where your array is restricted to list of list and the size of 'sublist' is consistent throughout i.e "3" in your case
m = [[1,2,3],[4,5,10],[6,2,1]] #input 2D array
a, b = 0, 2 #user input --> specific point a and b
flat_list_m = [item for firstlist in m for item in firstlist] #flat the 2D list
print (flat_list_m[len(m[0])*a+b:]) #print from specific position a and b, considering your sublist length is consistent throughout.
I hope this helps! :)

python - Comparing two lists to see if one occurs in another consecutively

I've been trying to make a function that can take two lists of any size (say, list A and list B) and sees if list B occurs in list A, but consecutively and in the same order. If the above is true, it returns True, else it'll return False
e.g.
A:[9,0,**1,2,3,4,5,6,**7,8] and B:[1,2,3,4,5,6] is successful
A:[1,2,0,3,4,0,5,6,0] and B:[1,2,3,4,5,6] is unsuccessful.
A:[1,2,3,4,5,6] and B [6,5,3,2,1,4] fails because despite having the same
numbers, they aren't in the same order
I've tried doing this using nested loops so far and am a bit confused as to where to go
Just try this:
L1 = [9,0,1,2,3,4,5,6,7,8]
L2 = [1,2,3,4,5,6]
c = 0
w = 0
for a in range(len(L2)):
for b in range(w+1, len(L1)):
if L2[a] == L1[b]:
c = c+1
w = b
break
else:
c = 0
if c == len(L2):
print('yes')
break
Here you check if the element of l2 is in l1 and if so breaks the first loops remember where you left and of the next element of l2 is the same as the next element of l1 and so on.
And the last part is to check if this happened as much times as the length of l2. if so then you know that the statement is correct!
if your arrays are not huge and if you can find a way to map each element in your array to a string you can use:
list1 = [9,0,1,2,3,4,5,6,7,8]
list2 = [1,2,3,4,5,6]
if ''.join(str(e) for e in list2) in ''.join(str(e) for e in list1):
print 'true'
it just make two string from the lists and than use 'in' to find any accorence
Use any function
any(A[i:i+len(B)] == B for i in range(len(A) - len(B) + 1))
demo
i converted the entire list into a string and then found a substring of that string
the list when converted to a string it becomes
str(a)='[9,0,1,2,3,4,5,6,7,8]'
which when when we strip the string becomes
str(a).strip('[]')='9,0,1,2,3,4,5,6,7,8'
Now the problem just converted to
checking if there is a substring in the the string
so we can us the in operator to check the substring
The solution
a=[9,0,1,2,3,4,5,6,7,8]
b=[1,2,3,4,5,6]
print(str(b).strip('[]') in str(a).strip(']['))
testcase1
testcase2
Try this:
L1 = [9,2,1,2,0,4,5,6,7,8]
L2 = [1,2,3,4,5,6]
def sameorder(L1,L2):
for i in range(len(L1)-len(L2)+1):
if L1[i:len(L2)+i]==L2:
return True
return False
You can create sublists of a that can be analyzed:
def is_consecutive(a, b):
return any(all(c == d for c, d in zip(b, i)) for i in [a[e:e+len(b)] for e in range(len(a)-len(b))])
cases = [[[9, 0, 1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6]], [[1, 2, 0, 3, 4, 0, 5, 6, 0], [1, 2, 3, 4, 5, 6]], [[1, 2, 3, 4, 5, 6], [6, 5, 3, 2, 1, 4]]]
final_cases = {"case_{}".format(i):is_consecutive(*a) for i, a in enumerate(cases, start=1)}
Output:
{'case_3': False, 'case_2': False, 'case_1': True}

Cycle a list from alternating sides

Given a list
a = [0,1,2,3,4,5,6,7,8,9]
how can I get
b = [0,9,1,8,2,7,3,6,4,5]
That is, produce a new list in which each successive element is alternately taken from the two sides of the original list?
>>> [a[-i//2] if i % 2 else a[i//2] for i in range(len(a))]
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
Explanation:
This code picks numbers from the beginning (a[i//2]) and from the end (a[-i//2]) of a, alternatingly (if i%2 else). A total of len(a) numbers are picked, so this produces no ill effects even if len(a) is odd.
[-i//2 for i in range(len(a))] yields 0, -1, -1, -2, -2, -3, -3, -4, -4, -5,
[ i//2 for i in range(len(a))] yields 0, 0, 1, 1, 2, 2, 3, 3, 4, 4,
and i%2 alternates between False and True,
so the indices we extract from a are: 0, -1, 1, -2, 2, -3, 3, -4, 4, -5.
My assessment of pythonicness:
The nice thing about this one-liner is that it's short and shows symmetry (+i//2 and -i//2).
The bad thing, though, is that this symmetry is deceptive:
One might think that -i//2 were the same as i//2 with the sign flipped. But in Python, integer division returns the floor of the result instead of truncating towards zero. So -1//2 == -1.
Also, I find accessing list elements by index less pythonic than iteration.
cycle between getting items from the forward iter and the reversed one. Just make sure you stop at len(a) with islice.
from itertools import islice, cycle
iters = cycle((iter(a), reversed(a)))
b = [next(it) for it in islice(iters, len(a))]
>>> b
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
This can easily be put into a single line but then it becomes much more difficult to read:
[next(it) for it in islice(cycle((iter(a),reversed(a))),len(a))]
Putting it in one line would also prevent you from using the other half of the iterators if you wanted to:
>>> iters = cycle((iter(a), reversed(a)))
>>> [next(it) for it in islice(iters, len(a))]
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
>>> [next(it) for it in islice(iters, len(a))]
[5, 4, 6, 3, 7, 2, 8, 1, 9, 0]
A very nice one-liner in Python 2.7:
results = list(sum(zip(a, reversed(a))[:len(a)/2], ()))
>>>> [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
First you zip the list with its reverse, take half that list, sum the tuples to form one tuple, and then convert to list.
In Python 3, zip returns a generator, so you have have to use islice from itertools:
from itertools import islice
results = list(sum(islice(zip(a, reversed(a)),0,int(len(a)/2)),()))
Edit: It appears this only works perfectly for even-list lengths - odd-list lengths will omit the middle element :( A small correction for int(len(a)/2) to int(len(a)/2) + 1 will give you a duplicate middle value, so be warned.
Use the right toolz.
from toolz import interleave, take
b = list(take(len(a), interleave((a, reversed(a)))))
First, I tried something similar to Raymond Hettinger's solution with itertools (Python 3).
from itertools import chain, islice
interleaved = chain.from_iterable(zip(a, reversed(a)))
b = list(islice(interleaved, len(a)))
If you don’t mind sacrificing the source list, a, you can just pop back and forth:
b = [a.pop(-1 if i % 2 else 0) for i in range(len(a))]
Edit:
b = [a.pop(-bool(i % 2)) for i in range(len(a))]
Not terribly different from some of the other answers, but it avoids a conditional expression for determining the sign of the index.
a = range(10)
b = [a[i // (2*(-1)**(i&1))] for i in a]
i & 1 alternates between 0 and 1. This causes the exponent to alternate between 1 and -1. This causes the index divisor to alternate between 2 and -2, which causes the index to alternate from end to end as i increases. The sequence is a[0], a[-1], a[1], a[-2], a[2], a[-3], etc.
(I iterate i over a since in this case each value of a is equal to its index. In general, iterate over range(len(a)).)
The basic principle behind your question is a so-called roundrobin algorithm. The itertools-documentation-page contains a possible implementation of it:
from itertools import cycle, islice
def roundrobin(*iterables):
"""This function is taken from the python documentation!
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) # next instead of __next__ for py2
while pending:
try:
for next in nexts:
yield next()
except StopIteration:
pending -= 1
nexts = cycle(islice(nexts, pending))
so all you have to do is split your list into two sublists one starting from the left end and one from the right end:
import math
mid = math.ceil(len(a)/2) # Just so that the next line doesn't need to calculate it twice
list(roundrobin(a[:mid], a[:mid-1:-1]))
# Gives you the desired result: [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
alternatively you could create a longer list (containing alternating items from sequence going from left to right and the items of the complete sequence going right to left) and only take the relevant elements:
list(roundrobin(a, reversed(a)))[:len(a)]
or using it as explicit generator with next:
rr = roundrobin(a, reversed(a))
[next(rr) for _ in range(len(a))]
or the speedy variant suggested by #Tadhg McDonald-Jensen (thank you!):
list(islice(roundrobin(a,reversed(a)),len(a)))
Not sure, whether this can be written more compactly, but it is efficient as it only uses iterators / generators
a = [0,1,2,3,4,5,6,7,8,9]
iter1 = iter(a)
iter2 = reversed(a)
b = [item for n, item in enumerate(
next(iter) for _ in a for iter in (iter1, iter2)
) if n < len(a)]
For fun, here is an itertools variant:
>>> a = [0,1,2,3,4,5,6,7,8,9]
>>> list(chain.from_iterable(izip(islice(a, len(a)//2), reversed(a))))
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
This works where len(a) is even. It would need a special code for odd-lengthened input.
Enjoy!
Not at all elegant, but it is a clumsy one-liner:
a = range(10)
[val for pair in zip(a[:len(a)//2],a[-1:(len(a)//2-1):-1]) for val in pair]
Note that it assumes you are doing this for a list of even length. If that breaks, then this breaks (it drops the middle term). Note that I got some of the idea from here.
Two versions not seen yet:
b = list(sum(zip(a, a[::-1]), ())[:len(a)])
and
import itertools as it
b = [a[j] for j in it.accumulate(i*(-1)**i for i in range(len(a)))]
mylist = [0,1,2,3,4,5,6,7,8,9]
result = []
for i in mylist:
result += [i, mylist.pop()]
Note:
Beware: Just like #Tadhg McDonald-Jensen has said (see the comment below)
it'll destroy half of original list object.
One way to do this for even-sized lists (inspired by this post):
a = range(10)
b = [val for pair in zip(a[:5], a[5:][::-1]) for val in pair]
I would do something like this
a = [0,1,2,3,4,5,6,7,8,9]
b = []
i = 0
j = len(a) - 1
mid = (i + j) / 2
while i <= j:
if i == mid and len(a) % 2 == 1:
b.append(a[i])
break
b.extend([a[i], a[j]])
i = i + 1
j = j - 1
print b
You can partition the list into two parts about the middle, reverse the second half and zip the two partitions, like so:
a = [0,1,2,3,4,5,6,7,8,9]
mid = len(a)//2
l = []
for x, y in zip(a[:mid], a[:mid-1:-1]):
l.append(x)
l.append(y)
# if the length is odd
if len(a) % 2 == 1:
l.append(a[mid])
print(l)
Output:
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]

Convert list of lists to list of integers

I need to convert a list of lists to a list of integers.
from:
L1 = [[1, 2, 3, 4], [3, 7, 1, 7], [0, 5, 6, 7], [9, 4, 5, 6]]
to:
L2 = [1234, 3717, 0567, 9456]
How can I make python recognize an integer starting with 0? Like the case L2[2]
The other question is, how can I check if items in a list are ordered?
A = [1, 2, 6, 9] ---->True
Other than this:
A == sorted(A)
You guys are FAST. Thanks!
The first question can be done by
L = [int("".join([str(y) for y in x])) for x in L]
Unfortunately, integers do not start with a 0. There is no way to do this.
Checking if A == sorted(A) is a perfectly fine way to do this.
L2 = [reduce(lambda x,y : 10 * x + y, l) for l in L1]
If you want a solution that doesn't go through strings.
For question 1, maybe Python 3 converts, but the plain .join give me a TypeError in Python 2. Try:
["".join(str(d) for d in x) for x in L]
...for the string representation, or
[int("".join(str(d) for d in x)) for x in L]
(The string representation is the only way to preserve leading zeros, except with formatted output...and then that's just string conversion on the way to the output file/string.)
For question 2: all(A[i-1] < A[i] for i in range(1, len(A)))
>>> A = [1, 2, 6, 9]
>>> all(A[i-1] < A[i] for i in range(1, len(A)))
True
You can do:
L2 = [ int("1" + "".join(str(l) for l in ll1)) for ll1 in L1 ]
to keep the zeros and get:
[11234, 13717, 10567, 19456]
but then you need to get rid of the leftmost 1s.

Categories