I am working on moving all zeroes to end of list. .. is this approach bad and computationally expensive?
a = [1, 2, 0, 0, 0, 3, 6]
temp = []
zeros = []
for i in range(len(a)):
if a[i] !=0:
temp.append(a[i])
else:
zeros.append(a[i])
print(temp+zeros)
My Program works but not sure if this is a good approach?
A sorted solution that avoids changing the order of the other elements is:
from operator import not_
sorted(a, key=not_)
or without an import:
sorted(a, key=lambda x: not x) # Or x == 0 for specific numeric test
By making the key a simple boolean, sorted splits it into things that are truthy followed by things that are falsy, and since it's a stable sort, the order of things within each category is the same as the original input.
This looks like a list. Could you just use sort?
a = [1, 2, 0, 0, 0, 3, 6]
a.sort(reverse=True)
a
[6, 3, 2, 1, 0, 0, 0]
To move all the zeroes to the end of the list while preserving the order of all the elements in one traversal, we can keep the count of all the non-zero elements from the beginning and swap it with the next element when a non-zero element is encountered after zeroes.
This can be explained as:
arr = [18, 0, 4, 0, 0, 6]
count = 0
for i in range(len(arr):
if arr[i] != 0:
arr[i], arr[count] = arr[count], arr[i]
count += 1
How the loop works:
when i = 0, arr[i] will be 18, so according to the code it will swap with itself, which doesn't make a difference, and count will be incremented by one. When i=1, it will have no affect as till now the list we have traversed is what we want(zero in the end). When i=4, arr[i]= 4 and arr[count(1)]= 0, so we swap them leaving the list as[18, 4, 0, 0, 0, 6] and count becomes 2 signifying two non-zero elements in the beginning. And then the loop continues.
You can try my solution if you like
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
for num in nums:
if num == 0:
nums.remove(num)
nums.append(num)
I have tried this code in leetcode & my submission got accepted using above code.
Nothing wrong with your approach, really depends on how you want to store the resulting values. Here is a way to do it using list.extend() and list.count() that preserves order of the non-zero elements and results in a single list.
a = [1, 2, 0, 0, 0, 3, 6]
result = [n for n in a if n != 0]
result.extend([0] * a.count(0))
print(result)
# [1, 2, 3, 6, 0, 0, 0]
You can try this
a = [1, 2, 0, 0, 0, 3, 6]
x=[i for i in a if i!=0]
y=[i for i in a if i==0]
x.extend(y)
print(x)
There's nothing wrong with your solution, and you should always pick a solution you understand over a 'clever' one you don't if you have to look after it.
Here's an alternative which never makes a new list and only passes through the list once. It will also preserve the order of the items. If that's not necessary the reverse sort solution is miles better.
def zeros_to_the_back(values):
zeros = 0
for value in values:
if value == 0:
zeros += 1
else:
yield value
yield from (0 for _ in range(zeros))
print(list(
zeros_to_the_back([1, 2, 0, 0, 0, 3, 6])
))
# [1, 2, 3, 6, 0, 0, 0]
This works using a generator which spits out answers one at a time. If we spot a good value we return it immediately, otherwise we just count the zeros and then return a bunch of them at the end.
yield from is Python 3 specific, so if you are using 2, just can replace this with a loop yielding zero over and over.
Numpy solution that preserves the order
import numpy as np
a = np.asarray([1, 2, 0, 0, 0, 3, 6])
# mask is a boolean array that is True where a is equal to 0
mask = (a == 0)
# Take the subset of the array that are zeros
zeros = a[mask]
# Take the subset of the array that are NOT zeros
temp = a[~mask]
# Join the arrays
joint_array = np.concatenate([temp, zeros])
I tried using sorted, which is similar to sort().
a = [1, 2, 0, 0, 0, 3, 6]
sorted(a,reverse=True)
ans:
[6, 3, 2, 1, 0, 0, 0]
from typing import List
def move(A:List[int]):
j=0 # track of nonzero elements
k=-1 # track of zeroes
size=len(A)
for i in range(size):
if A[i]!=0:
A[j]=A[i]
j+=1
elif A[i]==0:
A[k]=0
k-=1
since we have to keep the relative order. when you see nonzero element, place that nonzero into the index of jth.
first_nonzero=A[0] # j=0
second_nonzero=A[1] # j=1
third_nonzero=A[2] # j=2
With k we keep track of 0 elements. In python A[-1] refers to the last element of the array.
first_zero=A[-1] # k=-1
second_zero=A[-2] # k=-2
third_zero= A[-3] # k=-3
a = [4,6,0,6,0,7,0]
a = filter (lambda x : x!= 0, a) + [0]*a.count(0)
[4, 6, 6, 7, 0, 0, 0]
Related
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 5 months ago.
I need to loop over a list until list is not empty according to a condition, also
This is the code I wrote but is returning a 'list index out of range' error.
What is the problem? Any suggestions on how to eventually improve this?
l = [0, 1, 0, 0, 1, 1]
removed= []
while l:
for i in range(len(l)):
if l[i]>0:
l.remove(l[i])
# store the index processing order
removed.append(i)
else:
continue
l = [x+1 for x in l]
The problem is the list l gets smaller after calling l.remove(value), but subscript 'i' still try to index the original l.
Based on the above analysis, one solution is to keep l unchanged in the inner loop, the other is to keep the unseen i reduced along with l.
# Create new lists to keep `l` unchanged in the inner loop
def method1():
l = [0, 1, 0, 0, 1, 1]
removed= []
while l:
next_l = []
[next_l.append(v) if v <= 0 else removed.append(i) for i, v in enumerate(l)]
l = [x+1 for x in next_l]
return removed
def method2():
l = [0, 1, 0, 0, 1, 1]
removed= []
while l:
num_del = 0 # record number of deletions in the inner loop
for i in range(len(l)):
if l[i-num_del]>0:
l.remove(l[i-num_del])
num_del += 1
# store the index processing order
removed.append(i)
else:
continue
l = [x+1 for x in l]
return removed
assert method1() == method2()
# output [1, 4, 5, 0, 1, 2]
But I guess you expect the result [1, 4, 5, 0, 2, 3], i.e., record the processing order with subscript in the original list. If so, try this:
l = [0, 1, 0, 0, 1, 1]
el = list(enumerate(l))
removed = []
bound = 0
while len(removed) != len(l):
removed.extend(list(filter(lambda iv: iv[1] > bound, el)))
el = list(filter(lambda iv: iv[1] <= bound, el))
bound -= 1
removed, _ = zip(*removed)
IIUC - it looks like you just want the index of the removed values and keep the values in the original list if they are less than or equal to and then +1 to the value
l = [0, 1, 0, 0, 1, 1]. # your list
keep_idx, lst = zip(*[(idx, i+1) for idx, i in enumerate(l) if i<=0])
print(list(keep_idx)) # -> [0, 2, 3]
print(list(lst)). # -> [1, 1, 1]
Since you're removing items from list while trying to loop through it, your list length keeps going down but the range doesn't account for it. What you could do is looping through the list backwards.
One solution is using
range(len(list_name)-1, -1, -1)
This is to loop through the array backwards and stop the out of range error
EDIT: I was wrong here, but I will keep the answer up so people can see where I went wrong. As Ignatius pointed out, range() is exclusive of the final item.
Original answer:
Lists use Zero-based indexing, so the first item is item 0.
An example list:
l = [1, 2, 3, 4, 5]
l contains 5 items, but indexes start at 0, so '1' is l[0] and '5' is l[4].
This is why you're getting an out of range exception. In your code you are looping up to len(l) which is 6, but l[6] is out of bounds. l[5] is the final item in your list.
Solution:
for i in range(len(l)-1)):
Given an array, say array = [3,5,-1,4,2,-1,5,0,-1], I want to sort it such that everything stays in the same place, except the -1's which will move to the end, so:
>>> function(array)
[3,5,4,2,5,0,-1,-1,-1]
This could also be generalised to any number, however, I'm using -1 as a key value and need them to all be at the end of the array for some later processing.
I've tried playing with the sort() and sorted() inbuilt functions in python however neither of these seemed to be able to do what I needed.
You can use a lambda function in your call to sort. Here we sort the values by returning a 0 if the value is anything other than -1, otherwise 1. This will push all of the -1 values to the right.
array.sort(key=lambda x: x==-1)
array
# returns
[3, 5, 4, 2, 5, 0, -1, -1, -1]
This is one approach.
Ex:
array = [3,5,-1,4,2,-1,5,0,-1]
num = -1
print([i for i in array if i != num] + [i for i in array if i == num])
Output:
[3, 5, 4, 2, 5, 0, -1, -1, -1]
Python sort is stable, so you can just move some items around, leaving the others untouched
lst = [3,5,-1,4,2,-1,5,0,-1]
print(sorted(lst,key = lambda x : x == -1))
result: [3, 5, 4, 2, 5, 0, -1, -1, -1]
with this key, all items which are equal to -1 yield True for the sort key, making them rise to the end of the list. Others are left where they are relatively to each other (because sort key yields the same False value for all of them).
I'm trying to do the following in python: given a list of lists and an integer i
input = [[1, 2, 3, 4], [1, 2, 3, 4], [5, 6, 7, 8]]
i = 1
I need to obtain another list which has all 1s for the elements of the i-th list, 0 otherwise
output = [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0]
I wrote this code
output = []
for sublist in range(0, len(input)):
for item in range(0, len(input[sublist])):
output.append(1 if sublist == i else 0)
and it obviously works, but since I'm a newbie in python I suppose there's a better 'pythonic' way of doing this.
I thought using map could work, but I can't get the index of the list with it.
Creating extra variable to get index of current element in interation is quite unpythonic. Usual alternative is usage of enumerate built-in function.
Return an enumerate object. sequence must be a sequence, an iterator,
or some other object which supports iteration. The next() method of
the iterator returned by enumerate() returns a tuple containing a
count (from start which defaults to 0) and the values obtained from
iterating over sequence.
You may use list comprehension with double loop inside it for concise one liner:
input_seq = [[1, 2, 3, 4], [1, 2, 3, 4], [5, 6, 7, 8]]
i = 1
o = [1 if idx == i else 0 for idx, l in enumerate(input_seq) for _ in l]
Alternatively,
o = [int(idx == i) for idx, l in enumerate(input_seq) for _ in l]
Underscore is just throwaway name, since in this case we don't care for actual values stored in input sublists.
Here's a 1-liner, but it's not really obvious:
output = [int(j == i) for j, sublist in enumerate(input) for _ in sublist]
Somewhat more obvious:
output = []
for j, sublist in enumerate(input):
output.extend([int(i == j)] * len(sublist))
Then "0 or 1?" is computed only once per sublist, which may or may not be more efficient.
I am not looking to get an list inside list instead I just want to add zeros but do not know how. see code
def myconv(x,h):
q=[]
print(h)
for i in range(len_h):
q.append(h[len_h-1-i])
print(q)
q.insert(0,([0]*len_h)) #I want this to add just plain zeros to the array... not an array insdie an
#like it is doing here
q.insert(len(q)+1,(0]*len_h))
print(q)
print(myconv([6,7,8,9],[1,2,3,4,5,6,7,8,9,0]))
You want to use +, e.g. [0, 0, 0] + [2, 1, 3] == [0, 0, 0, 2, 1, 3], to concatenate an array onto the first. Otherwise, you'll need to insert (or append) items one at a time.
To demonstrate how list multiplication works:
>>> [0]*2
[0, 0]
>>> [0]*5
[0, 0, 0, 0, 0]
I prefer the in-place extend operation. Just don't assign to it, because like most all Python in-place methods, it returns None.
>>> l2.extend([0]*5)
>>> l2
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0]
You have two ways of doing this:
q = [0]*len_h + q
or
q[0:0] = [0]*len_h
q.insert(0,([0]*len_h))
This creates a list within a list because [0]*len_h creates a new list. This array is then inserted into q. Instead, insert each element into q without creating a new list.
for i in range(len_h):
q.insert(0, 0)
Hey I am writing a function that takes a matrix input such as the one below and returns its inverse, where all the 1s are changed to 0s and all the 0s changed to 1s, while keeping the diagonal from top left to bottom right 0s.
An example input:
g1 = [[0, 1, 1, 0],
[1, 0, 0, 1],
[1, 0, 0, 1],
[0, 1, 1, 0]]
the function should output this:
g1 = [[0, 0, 0, 1],
[0, 0, 1, 0],
[0, 1, 0, 0],
[1, 0, 0, 0]]
When I run the program, it throws a "list index out of range" error. I'm sure this is because the loops I have set up are trying to access values that do not exist, but how do I allow an input of unknown row and column size? I only know how to do this with a single list, but a list of lists? Here is the function, not including the test function that calls it:
def inverse_graph(graph):
# take in graph
# change all zeros to ones and ones to zeros
r, c = 0, 0 # row, column equal zero
while (graph[r][c] == 0 or graph[r][c] == 1): # while the current row has a value.
while (graph[r][c] == 0 or graph[r][c] == 1): # while the current column has a value
if (graph[r][c] == 0):
graph[r][c] = 1
elif (graph[r][c] == 1):
graph[r][c] = 0
c+=1
c=0
r+=1
c=0
r=0
# sets diagonal to zeros
while (g1[r][c] == 0 or g1[r][c] == 1):
g1[r][c]=0
c+=1
r+=1
return graph
This doesn't directly answer your question, but I want to point out that in Python you can often reduce and sometimes eliminate the need to use indexing by using a
for <element> in <container>:
statement. By use it along with the built-in enumerate() function, it's possible to get both the index and the corresponding element
for <index>,<element> in enumerate(<container>):
Applying them to your problem would allow something like this:
g1 = [[0, 1, 1, 0],
[1, 0, 0, 1],
[1, 0, 0, 1],
[0, 1, 1, 0]]
def inverse_graph(graph):
""" invert zeroes and ones in a square graph
but force diagonal elements to be zero
"""
for i,row in enumerate(graph):
for j,cell in enumerate(row):
row[j] = 0 if cell or i == j else 1
return graph
print(g1)
print(inverse_graph(g1))
Output:
[[0, 1, 1, 0], [1, 0, 0, 1], [1, 0, 0, 1], [0, 1, 1, 0]]
[[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]]
Which is simpler and clearly works. Another point is that since you're applying the function to a mutable (changeable) container, a list-of-lists, there's really no need to return the container because it is being changed in-place. It's not wrong to do so because it can make using the function easier, but it's something you may not have realized.
You could shorten the function a little bit more and eliminate indexing altogether by using something called a list comprehension:
def inverse_graph(graph):
return [[0 if cell or i == j else 1
for j,cell in enumerate(row)]
for i,row in enumerate(graph)]
Because of the way they work, this version doesn't change the graph in-place, but instead creates and returns a new one.
while (graph[r][c] == 0 or graph[r][c] == 1): # while the current row has a value.
You have to make sure first, that both indices exist, prior to comparing its -possible- value to 0 or 1. This causes your exceptions. To invert your matrix you would want to do something like
for row in graph:
for idx, v in enumerate (row):
row [idx] = 0 if v else 1
The mistake is in your "while the current row has a value". This will be always true while you will iterate through the elements in the row, and when you'll reach past them, you'll get the exception.
Instead, use:
for r in range(len(graph):
for c in range(len(graph[0]):
# do something with graph[r][c]
It is fairly simple.
Basically you need to find the number of elements in the array
mylist = [1,2,3,4,5]
len(mylist) # returns 5
#this gives the number of elements.
rows=len(g1) # get the number of rows
columns=len(g1[0]) #get the number of columns
#Now iterate over the number of rows and columns
for r in range(0, rows):
for c in range (0,columns):
if (r==c):
g1[r][c]=0
else:
g1[r][c]=1-g1[r][c]
Hope that helps
Not an answer to your question but here is a 'easy' way to do it
return [[0 if i2==i else 1 if item == 0 else 0 for i2,item in enumerate(row)] for i,row in graph]