Given a list of lists (ie a nested list), I want to delete all the inner lists with length 1 from the beginning and the end of the outer list. For example
d = [[3], [1,5], [3], [3,2,4], [8], [3]]
should become:[1,5], [3], [3,2,4]
.
For the beginning i am using the following code:
i = 0
while len(d[i]) == 1:
d.remove(d[i])
n = n -1
and for the end i use the code:
while len(d[n-1]) == 1:
d.remove(d[n-1])
n = n - 1
where n = number of lists.
When i run this i get:
[[1, 5], [3, 2, 4]]
So it also removes the lists in the middle with length 1. How can i change the code so it does not do that.
So for an n number of lists, i want to remove all the lists at the beginning which have 1 length, till there is one list that does not have lenght one. The same I want at the end.
CodePudding user response:
Most of the current answers involve repeated popping from the front of a list, which is slow.
Using the answers here to find the first and last index where a condition does not hold, we can find the start and end of the desired sublist in linear time. Then, we construct the slice using the found start and end indices. If the list contains only sublists of length 1, then our generators will raise a StopIteration
-- to avoid this, we check ahead of time whether all of the lists are of length 1:
if all(len(item) == 1 for item in data):
result = []
else:
start = next(i for i, v in enumerate(data) if len(v) != 1)
end = len(data) - next(i for i, v in enumerate(reversed(data)) if len(v) != 1)
result = data[start:end]
print(result)
This outputs:
[[1, 5], [3], [3, 2, 4]]
CodePudding user response:
First and foremost, don't modify lists you are iterating over, you can simply keep track of the indices where the condition is met and then slice your list accordingly:
def filter_list(list_of_list, len_to_filter):
l, r = 0, -1 # left, right indices
n = len(list_of_list) // 2 - 1
for i in range(n):
if len(list_of_list[i]) == len_to_filter:
l = 1
if len(list_of_list[-i-1]) == len_to_filter:
r -= 1
return list_of_list[l:r 1]
filter_list(d, 1)
This doesn't mutate the original list and does not require any sorting or reversal. You just traverse the list up to its middle from each side.
CodePudding user response:
here is a simple solution:
def removeFunction(d):
# remove length 1 elements from beginning
for i in range(len(d)):
if len(d[i]) > 1:
l = i
break
# remove length 1 elements from the end
for i in range(len(d)-1, -1, -1):
if len(d[i]) > 1:
r = i
break
return d[l:r 1]
CodePudding user response:
remove() removes the first encountered element given in the function paramater.
So in your case your code that removes the last element first checks the elements from beginning of the list, because of that it removes [3] from middle.
For that part you can use pop() or del which removes the element from given index:
while len(d[n-1]) == 1:
d.pop()
n = n - 1
Note: You can use pop() without an argument in this case, because it removes last item from the list when there is no given argument.
CodePudding user response:
There's no need for i
or n
since you're modifying the list, so its first and last elements change, not their indices. You can do something along these lines:
while len(d[0]) == 1:
d.pop(0) # remove from head
while len(d[-1]) == 1:
d.pop() # remove from tail
You'll also need to do some error-checking to ensure that d[0]
and d[-1]
exist, though.
CodePudding user response:
In your code, when removing with d.remove(d[n-1]) it removes from your list d everything that matches with d[n-1]. So the value '[3]' is being removed from everywhere.
Try this instead:
d = [[3], [1,5], [3], [3,2,4], [8], [3]]
while len(d[0]) == 1 or len(d[-1]) == 1:
if len(d[0]) == 1:
d.pop(0)
elif len(d[-1]) == 1:
d.pop(-1)