So say I have, list1 = ['the dog', 'the cat', 'cat dog', 'the dog ran home']
and sub_string = 'the dog'
how can I return list2 = ['the dog', 'the cat', 'cat dog']
i.e, return a list with the last occurrence of the substring removed?
So say I have, list1 = ['the dog', 'the cat', 'cat dog', 'the dog ran home']
and sub_string = 'the dog'
how can I return list2 = ['the dog', 'the cat', 'cat dog']
i.e, return a list with the last occurrence of the substring removed?
No built-in will help much you here, since scanning for a substring in a list
isn't a supported feature, and doing so in reverse order is doubly hard. List comprehensions won't do much good either, since making them stateful enough to recognize when you've found your needle would involve adding side-effects to the list comprehension, which makes it cryptic and violates the purpose of functional programming tools. So you're stuck doing the looping yourself:
list2 = []
list1iter = reversed(list1) # Make a reverse iterator over list1
for item in list1iter:
if sub_string in item: # Found item to remove, don't append it, we're done
break
list2.append(item) # Haven't found it yet, keep item
list2.extend(list1iter) # Pull all items after removed item
list2.reverse() # Put result back in forward order
An alternative approach would be to scan by index, allowing you to del
it; this might be a better solution if you want to modify list1
in place, rather than making a new list
:
for i, item in enumerate(reversed(list1), 1):
if sub_string in item:
del list1[-i]
break
That solution is adaptable to making a new copy by simply changing all references to list1
to list2
, and adding list2 = list1[:]
before the loop.
In both cases, you can detect if an item was found at all by putting an else:
block on the for
; if the else
block triggers, you didn't break
, because sub_string
wasn't found anywhere.
the problem statement is to: remove the element with the substring as the query
so, as I deduce it has two steps.
for pattern matching, we can use re
module (we can use in
as well as mentioned in ShadowRanger's answers)
import re
pattern = re.compile('the dog') # target pattern
my_list = ['the dog', 'the cat', 'cat dog', 'the dog ran home'] # our list
my_list = enumerate(my_list) # to get indexes corresponding to elemnts i.e. [(0, 'the dog'), (1, 'the cat'), (2, 'cat dog'), (3, 'the dog ran home')]
elems = list(filter(lambda x: pattern.search(x[1]), my_list) # match the elements in the second place and filter them out, remember filter in python 3.x returns an iterator
print(elems) # [(0, 'the dog'), (3, 'the dog ran home')]
del my_list[elems[-1][0]] # get the last element and take the index of it and delete it.
EDIT
As ShadowRunner suggested, we can optimize the code with the use of list comprehension with if statement instead of filter
function.
elems = [i for i, x in enumerate(my_list) if pattern.search(x)]
You could do it in two steps:
Example:
needle = 'the dog'
haystack = ['the dog', 'the cat', 'cat dog', 'the dog ran home']
last = max(loc for loc, val in enumerate(haystack) if needle in val)
result = [e for i, e in enumerate(haystack) if i != last]
print(result)
Output
['the dog', 'the cat', 'cat dog']
For more details on finding the index of the last occurrence see this.
list1 = ['the dog', 'the cat','the dog me', 'cat dog']
sub_string = 'the dog'
for i in list1[::-1]:
print(i)
if sub_string in i:
list1.remove(i)
break
output ['the dog', 'the cat', 'the dog me', 'cat dog']
One solution is to traverse the input in reverse order and find the index in the reversed list. After that, use the index to slice the input list1
.
idx = next(i for i, s in enumerate(reversed(list1), 1) if sub_string in s)
list2 = list1[:-idx] # If in-place updates are intended, use `del list1[-idx:]` instead