python list and sublist - python

I have to check if list1 is contained in list2. It also should check if it appears in that order in the list as well. If it's true, it should return true and false if it doesn't.
def check(lst1, lst2):
for x in lst1:
for y in lst2:
xx = lst1.sort()
yy = lst2
if xx != yy:
return False
else:
return True
I'm confusing myself with the for loops and also, I don't know where to go from here to fix my code. Pointers please?
example of what it should do:
check([4,0],[9,1,4,6,8,9])
True
check([1,2],[2,3,1])
False

I thought the problem was begging to be solved recursively, so I did:
def check(needle, haystack):
if not needle:
return True
try:
offset = haystack.index(needle[0])
return check(needle[1:], haystack[offset+1:])
except:
return False
Edit:
Brief explanation:
First we check if the list we're looking for is empty (this is important once we start calling ourselves), since all lists have the empty list inside it, we return True. Then we try finding the first item of the list we're looking for in the list we're looking at. If we find it, we then call the function again, but change the arguments a bit: we already looked at the first item of the 'needle' and saw it in the 'haystack'. So now we need to check if the remainder of 'needle' is in the remainder of 'haystack'. So we call the function again only with the remainder of both lists. The remainder of needle is everything but the first element, and the remainder of haystack is all items after the one we found. If we get to a point where the first list is empty, it means we found it in the haystack. If we get an exception, what we were looking for wasn't found, so we return False, which bubbles up the call stack and gets returned.

You could start with something like:
set(lst1).issubset(lst2)
to see if lst1 is contained within lst2 ignoring order. If it passes the test that one list is contained within the other, then you could do something like:
ii = lst2.index(lst1[0])
if lst2[ii:ii+len(lst1)] == lst1:
return True
else:
return False
Originally I stated that the first check was irrelevant given the second, although you'd have to properly handle the ValueError if the first element of lst1 is not in lst2.
Edit:
Just as a side note, I compared a version of my code vs yan's and mine is significantly faster under almost all use cases, especially if len(lst1) is larger (up to 200x speedup vs yan's implementation). Give it a try with the timeit module.
def check(lst1,lst2):
try:
ii = lst2.index(lst1[0])
except ValueError:
return False
if lst2[ii:ii+len(lst1)] == lst1:
return True
else:
return False
As an explanation of how it works ii = lst2.index(lst1[0]) finds the index in lst2 that matches the first element of lst1. If that item is missing from lst2 it catches the ValueError and returns False. If that element does exist, lst2[ii:ii+len(lst1)] == lst1 compares all of lst1 vs the sublist of lst2 starting from the matched element and taking the next len(lst) elements.

>>> a=[9,1,4,6,8,9]
>>> by_twos=zip(a, a[1:])
>>> by_twos
[(9, 1), (1, 4), (4, 6), (6, 8), (8, 9)]
>>> (4,6) in by_twos
True

Related

Built in (remove) function not working with function variable

Have a good day everyone, pardon my lack of understanding, but I can't seem to figure out why python built in function does not work when being called with another function variable and it just doesn't do what I want at all. Here is the code
def ignoreten(h):
ignoring = False
for i in range (1,len(h)-2):
if ignoring == True and h[i]==10:
h.remove(10)
if ignoring == False and h[i] ==10:
ignoring = True
The basic idea of this is just to decided the first 10 in a list, keep it, continue iterating until you faced another 10, then just remove that 10 to avoid replication, I had searched around but can't seem to find any solution and that's why I have to bring it up here. Thank you
The code you listed
def ignoreten(h):
ignoring = False
for i in range (1,len(h)-2):
if ignoring == True and h[i]==10:
h.remove(10)
if ignoring == False and h[i] ==10:
ignoring = True
Will actually do almost the exact opposite of what you want. It'll iterate over h (sort of, see [1]), and if it finds 10 twice, it'll remove the first occurrence from the list. (And, if it finds 10 three times, it'll remove the first two occurrences from the list.)
Note that list.remove will:
Remove the first item from the list whose value is equal to x. It
raises a ValueError if there is no such item.
Also note that you're mutating the list you're iterating over, so there's some additional weirdness here which may be confusing you, depending on your input.
From your follow-up comment to my question, it looks like you want to remove only the second occurrence of 10, not the first and not any subsequent occurrences.
Here are a few ways:
Iterate, store index, use del
def ignoreten(h):
index = None
found_first = False
for i,v in enumerate(h):
if v == 10:
if not found_first:
found_first = True
else:
index = i
break
if index is not None:
del h[index]
A little more verbose than necessary, but explicit, safe, and modifiable without much fear.
Alternatively, you could delete inside the loop but you want to make sure you immediately break:
def ignoreten(h):
found_first = False
for i,v in enumerate(h):
if v == 10:
if not found_first:
found_first = True
else:
del h[i]
break
Collect indices of 10s, remove second
def ignoreten(h):
indices = [i for (i,v) in enumerate(h) if v == 10]
if len(indices) > 1:
del h[indices[1]] # The second index of 10 is at indices[1]
Clean, but will unnecessarily iterate past the second 10 and collect as many indices of 10s are there are. Not likely a huge issue, but worth pointing out.
Collect indices of 10s, remove second (v2, from comments)
def ignoreten(h):
indices = [i for (i,v) in enumerate(h) if v == 10]
for i in reversed(indices[1:]):
del h[i]
From your comment asking about removing all non-initial occurrences of 10, if you're looking for in-place modification of h, then you almost definitely want something like this.
The first line collects all the indices of 10 into a list.
The second line is a bit tricky, but working inside-out it:
[1:] "throws out" the first element of that list (since you want to keep the first occurrence of 10)
reversed iterates over that list backwards
del h[i] removes the values at those indices.
The reason we iterate backwards is because doing so won't invalidate the rest of our indices that we've yet to delete.
In other words, if the list h was [1, 10, 2, 10, 3, 10], our indices list would be [1, 3, 5].
In both cases we skip 1, fine.
But if we iterate forwards, once we delete 3, and our list shrinks to 5 elements, when we go to delete 5 we get an IndexError.
Even if we didn't go out of bounds to cause an IndexError, our elements would shift and we'd be deleting incorrect values.
So instead, we iterate backwards over our indices, delete 5, the list shrinks to 5 elements, and index 3 is still valid (and still 10).
With list.index
def ignoreten(h):
try:
second_ten = h.index(10, h.index(10)+1)
del h[second_ten]
except ValueError:
pass
The inner .index call finds the first occurrence, the second uses the optional begin parameter to start searching after that. Wrapped in try/except in case there are less than two occurrences.
⇒ Personally, I'd prefer these in the opposite order of how they're listed.
[1] You're iterating over a weird subset of the list with your arguments to range. You're skipping (not applying your "is 10" logic to) the first and last two elements this way.
Bonus: Walrus abuse
(don't do this)
def ignoreten(h):
x = 0
return [v for v in h if v != 10 or (x := x + 1) != 1]
(unlike the previous versions that operated on h in-place, this creates a new list without the second occurrence of 10)
But the walrus operator is contentious enough already, please don't let this code out in the wild. Really.

Verify with a for loop if numbers are increasing in a list

I have tried to create a function helping me to specify if my list "inc" is a list composed of increasing numbers. In the end, if I have a list of [1,2,3], I am expecting output as True and False when I have a list like [1,2,1]. With the following code, I get not the expected output which should be True.
inc=[1, 2, 3]
def increase(inc):
result = True
for i in range(0, len(inc)-1):
if inc[i] > inc[i-1]:
return result
else:
return False
print(increase(inc))
output=False
How about using sorted() function that returns a sorted list?
inc=[1, 2, 3] # try swapping items in the list
if sorted(inc) == inc:
print(True)
else:
print(False)
There are a few problems here. It returns False because lst[-1] is indeed bigger than lst[0]; you should start the range from 1.
In addition, it should return True only if the condition is True for all the iterations, not right after the first iteration.

Right code but still test case is not running in python

Given an array of ints, return True if .. 1, 2, 3, .. appears in the array somewhere.
def array123(nums):
for i in nums:
if nums[i:i+3] == [1,2,3]:
return True
return False
Coding bat problem
my code is satisfying all the test cases except for nums=[1,2,3]
can someone tell me whats wrong with my code
It should be like this.
def array123(nums):
for i in range(len(nums)):
if nums[i:i+3] == [1,2,3]:
return True
return False
Try this. Hope this helps. :)
Your code is not completely right. You're slicing the list with the items in the list, not with indices. Luckily, this did not throw any error because the items in the list are within the bounds of the list's indices or rather slicing does not raise errors, when done correclty, irrespective of start and/or stop indices.
You can use range(len(...)) to generate the indices, and you may stop the search at len(nums) - len(sublist), so you don't check for slices less than the length of the sublist. This comes more handy as the length of the sublist gets larger.
def array123(nums, sublist):
j = len(sublist)
for i in range(len(nums)-j):
if nums[i:i+j] == sublist:
return True
return False
# Call function
array123(nums, [1,2,3])
Useful reference:
Explain Python's slice notation
You're getting wrong result because you're iterating on the value of elements not on the index of elements of elements. Try following code
def array123(nums):
for i in range(len(nums)-2):
if nums[i:i+3] == [1,2,3]:
return True
return False
And remember to give the end index of list (range(len(nums)-2)) because suppose if length of your array is 4 then (range(len(nums)-2)) will be
(range(2)) = [0,1]
So the loop will iterate for 0,1 as starting index

In this short recursive function `list_sum(aList)`, the finish condition is `if not aList: return 0`. I see no logic in why this condition works

I am learning the recursive functions. I completed an exercise, but in a different way than proposed.
"Write a recursive function which takes a list argument and returns the sum of its integers."
L = [0, 1, 2, 3, 4] # The sum of elements will be 10
My solution is:
def list_sum(aList):
count = len(aList)
if count == 0:
return 0
count -= 1
return aList[0] + list_sum(aList[1:])
The proposed solution is:
def proposed_sum(aList):
if not aList:
return 0
return aList[0] + proposed_sum(aList[1:])
My solution is very clear in how it works.
The proposed solution is shorter, but it is not clear for me why does the function work. How does if not aList even happen? I mean, how would the rest of the code fulfill a not aList, if not aList means it checks for True/False, but how is it True/False here?
I understand that return 0 causes the recursion to stop.
As a side note, executing without if not aList throws IndexError: list index out of range.
Also, timeit-1million says my function is slower. It takes 3.32 seconds while the proposed takes 2.26. Which means I gotta understand the proposed solution.
On the call of the function, aList will have no elements. Or in other words, the only element it has is null. A list is like a string or array. When you create a variable you reserve some space in the memory for it. Lists and such have a null on the very last position which marks the end so nothing can be stored after that point. You keep cutting the first element in the list, so the only thing left is the null. When you reach it you know you're done.
If you don't use that condition the function will try to take a number that doesn't exist, so it throws that error.
You are counting the items in the list, and the proposed one check if it's empty with if not aList this is equals to len(aList) == 0, so both of you use the same logic.
But, you're doing count -= 1, this has no sense since when you use recursion, you pass the list quiting one element, so here you lose some time.
According to PEP 8, this is the proper way:
• For sequences, (strings, lists, tuples), use the fact that empty
sequences are false.
Yes: if not seq:
if seq:
No: if len(seq)
if not len(seq)
Here is my amateur thougts about why:
This implicit check will be faster than calling len, since len is a function to get the length of a collection, it works by calling an object's __len__ method. This will find up there is no item to check __len__.
So both will find up there is no item there, but one does it directly.
not aList
return True if there is no elements in aList. That if statement in the solution covers edge case and checks if input parameter is not empty list.
For understand this function, let's run it step by step :
step 0 :
L=[0,1,2,3,4]
proposed_sum([0,1,2,3,4])
L != []
return l[0] + proposed_sum([1,2,3,4])
step 1 calcul proposed_sum([1,2,3,4]):
proposed_sum([1,2,3,4])
L != []
return l[0] + sum([2,3,4])
step 2 calcul proposed_sum([2,3,4]):
proposed_sum([2,3,4])
L != []
return l[0] + sum([3,4])
step 3 calcul proposed_sum([3,4]):
proposed_sum([3,4])
L != []
return l[0] + sum([4])
step 4 calcul proposed_sum([4]):
proposed_sum([4])
L != []
return l[0] + sum([])
step 5 calcul proposed_sum([]):
proposed_sum([])
L == []
return 0
step 6 replace:
proposed_sum([0,1,2,3,4])
By
proposed_sum([]) + proposed_sum([4]) + proposed_sum([3,4]) + proposed_sum([2,3,4]) + proposed_sum([1,2,3,4])+ proposed_sum([0,1,2,3,4])
=
(0) + 4 + 3 + 2 + 1 + 0
Python considers as False multiple values:
False (of course)
0
None
empty collections (dictionaries, lists, tuples)
empty strings ('', "", '''''', """""", r'', u"", etc...)
any other object whose __nonzero__ method returns False
in your case, the list is evaluated as a boolean. If it is empty, it is considered as False, else it is considered as True. This is just a shorter way to write if len(aList) == 0:
in addition, concerning your new question in the comments, consider the last line of your function:
return aList[0] + proposed_sum(aList[1:])
This line call a new "instance" of the function but with a subset of the original list (the original list minus the first element). At each recursion, the list passed in argument looses an element and after a certain amount of recursions, the passed list is empty.

In Python, how can I find the index of the first item in a list that is NOT some value?

Python's list type has an index(x) method. It takes a single parameter x, and returns the (integer) index of the first item in the list that has the value x.
Basically, I need to invert the index(x) method. I need to get the index of the first value in a list that does NOT have the value x. I would probably be able to even just use a function that returns the index of the first item with a value != None.
I can think of a 'for' loop implementation with an incrementing counter variable, but I feel like I'm missing something. Is there an existing method, or a one-line Python construction that can handle this?
In my program, the situation comes up when I'm handling lists returned from complex regex matches. All but one item in each list have a value of None. If I just needed the matched string, I could use a list comprehension like '[x for x in [my_list] if x is not None]', but I need the index in order to figure out which capture group in my regex actually caused the match.
Exiting at the first match is really easy: instead of computing a full list comprehension (then tossing away everything except the first item), use next over a genexp. Assuming for example that you want -1 when no item satisfies the condition of being != x,
return next((i for i, v in enumerate(L) if v != x), -1)
This is Python 2.6 syntax; if you're stuck with 2.5 or earlier, .next() is a method of the genexp (or other iterator) and doesn't accept a default value like the -1 above (so if you don't want to see a StopIteration exception you'll have to use a try/except). But then, there is a reason more releases were made after 2.5 -- continuous improvement of the language and its built-ins!-)
Using a list comprehension when you only need the first just feels slimy (to me). Use a for-loop and exit early.
>>> lst = [None, None, None, "foo", None]
>>> for i, item in enumerate(lst):
... if item: break
... else:
... print "not found"
...
>>> i
3
enumerate() returns an iterator that yields a tuple of the current index of the iterable as well as the item itself.
[i for i, x in enumerate(my_list) if x != value][0]
If you're not sure whether there's a non-matching item, use this instead:
match = [i for i, x in enumerate(my_list) if x != value]
if match:
i = match[0]
# i is your number.
You can make this even more "functional" with itertools, but you will soon reach the point where a simple for loop is better. Even the above solutions aren't as efficient as a for loop, since they construct a list of all non-matching indices before you pull the one of interest.
A silly itertools-based solution:)
import itertools as it, operator as op, functools as ft
def index_ne(item, sequence):
sequence= iter(sequence)
counter= it.count(-1) # start counting at -1
pairs= it.izip(sequence, counter) # pair them
get_1st= it.imap(op.itemgetter(0), pairs) # drop the used counter value
ne_scanner= it.ifilter(ft.partial(op.ne, item), get_1st) # get only not-equals
try:
ne_scanner.next() # this should be the first not equal
except StopIteration:
return None # or raise some exception, all items equal to item
else:
return counter.next() # should be the index of the not-equal item
if __name__ == "__main__":
import random
test_data= [0]*20
print "failure", index_ne(0, test_data)
index= random.randrange(len(test_data))
test_data[index]= 1
print "success:", index_ne(0, test_data), "should be", index
All this just to take advantage of the itertools.count counting :)

Categories