Python - function find list in list - python

I wrote the function to find list A is in List B, the code as below:
def isinlist(l1,l2):
flag=1
for i in range(0,len(l1)+1):
if l1[i] not in l2:
flag = 0
return flag
a1 = ['hello','hi','123']
a2 = ['no worry','hello','hi','123'' ]# f1 = isinlist()
print(isinlist(a1,a2))
it show the error as below
IndexError: list index out of range
I thought the error come from l1 and l2 wasn't defined range in the function? Can someone help advise on this ?

Just remove the +1 from for i in range(0,len(l1)+1): and you are good to go:
def isinlist(l1,l2):
flag=1
for i in range(0,len(l1)):
if l1[i] not in l2:
flag = 0
return flag
a1 = ['hello','hi','123']
a2 = ['no worry','hello','hi','123' ]
print(isinlist(a1,a2))
To find elements in list2 that are not in list1:
def contains(l1, l2):
not_in__list_value=[]
for i in range(len(l2)-len(l1)+1):
for j in range(len(l1)):
if l2[i+j] != l1[j]:
not_in__list_value.append(l2[i+j] )
break
else:
return not_in__list_value
return not_in__list_value
a1 = ['hello','hi','123']
a2 = ['no worry','hello','hi','123' ]
f1 = contains(a1,a2)
print("value that not in list1 ",f1)

The error you get already gives you a hint what is going wrong. Simply remove the +1 in range(0, len(l1)+1). Otherwise the function tries to access an index in the array, which is not defined.
def isinlist(l1,l2):
flag=1
for i in range(0,len(l1)):
if l1[i] not in l2:
flag = 0
return flag
or what I would prefer (since range() starts at 0 from default)
def isinlist(l1,l2):
flag=1
for i in range(len(l1)):
if l1[i] not in l2:
flag = 0
return flag

the issue id in the line
for i in range(0,len(l1)+1):
in this loop i will eventually be larger than the length of l1 and thus l1[i] will be out of range
instead use
for i in range(len(l1)):
(range() starts at 0 by default)

I noticed that previous answers were not really pythonic and also checking only the presence of the elements.
Since you are learning python you definitely need to see how a pythonic solution would look like:
def is_in_list(left, right):
"""Returns true if all elements of left list are present also on right"""
return set(left).issubset(set(right))
Please note that we are using snake_case to separate words and also the super power of sets.
Another pythonic way to achieve this, suggested by #Tomerikoo, which is more memory efficient:
def is_in_list(left, right):
"""Returns true if all elements of left list are present also on right"""
return all(x in right for x in left).

Related

Binary Search in Python - Iterative Method

so I'm trying to learn programming myself in Python language and was confused implementing Binary Search in Python. So here's what I have done
list = [3,6,8,12,14,17,25,29,31,36,42,47,63,55,62]
key = 42
print(list)
def high(sorted_list):
max_index = len(sorted_list)
return max_index
def low(sorted_list):
start_index = 0
return start_index
def mid(sorted_list):
mid_index = ( low(sorted_list) + (high(sorted_list) - low(sorted_list)) ) // 2
return mid_index
for x in range(4):
if list[mid(list)] < key:
list = list[mid(list)+1:]
elif list[mid(list)] < key:
list = list[mid(list)-1:]
print(list)
I know I should not keep a range number in for loop but I know it will only make 4 comparisons in this example so I hardcoded it. But when I run it, it splits the list only once and keep on printing the second half of the list. Output image:
Ok, I tried your code and had to do a few corrections:
The while loop had to be modified (you knew that)
There wasn't a check for the case where the key is found (see comments)
There was a typo, < instead of > (see comments)
In the same line, the list partition was wrong
The low function was useless (returning a constant value) (see comments)
The high function was useless too (simply returning the value from another function)
The mid function was more complicated than needed (it boiled down to taking a value, then adding and subtracting zero), so it can simply take the value
Ah, btw the input list is not sorted in your example.
This is my proposal:
def mid(lst):
return len(lst) // 2
def bin_search(lst, k):
while lst:
i = mid(lst)
if lst[i] == k:
return True
if lst[i] < k:
lst = lst[i+1:]
elif lst[i] > k:
lst = lst[:i]
else:
return False
bin_search([3,6,8,12,14,17,25,29,31,36,42,47,55,62,63], 42)
True
bin_search([3,6,8,12,14,17,25,29,31,36,42,47,55,62,63], 58)
False

Python - Removing first two occurrences of element in list

The objective of this function is to remove the first two occurrences of n in a list.
Below is a code I had written but I still got it wrong after many hours. A friend advised me not to edit a list while iterating. However, I'm still stuck.
def remove_first_two(list,n):
if list == []:
return []
else:
count = 0
for ele in list:
if ele == n:
list.remove(ele)
count += 1
if count == 2:
break
return list
list = [1,2,2,3]
print(remove_first_two(list,2)) => [1,2,3] instead of [1,3]
Use list.remove twice with try-except. That will delete first two entries. Complexity O(n)
list_a = [1,2,3,4]
try:
list_a.remove(n)
list_a.remove(n)
# run a loop too, if it's more than 2
except:
pass
You can try find all indexes and del:
a = [1,2,3,2,3,2,4]
indices = [i for i, x in enumerate(a) if x == 2]
print(indices)
[1, 3, 5]
del a[indices[0]], a[indices[1]]
print(a)
[1, 3, 2, 2, 4]
First, don't use 'list' as its a key word in Python. Use something else, like 'alist'.
The code below does what you want and keeps the basic form of what you already have. You can of course also use the built-in .remove() method.
def remove_first_two(alist, n):
if alist == []:
return []
else:
count = 0
while count < 2:
for ele in alist:
if ele == n:
alist.remove(ele)
count += 1
return alist
alist = [1,2,2,3]
print(remove_first_two(alist,2)) # Output -> [1,3]
When your friend says "do not edit a list while iterating," he/she is right, and what he/she means is that you should create another list all together. What you are looking to do is the following:
def remove_first_two(list, n):
if list == []:
return []
else:
new_list = []
count = 0
for ele in list:
if ele == n:
if count >= 2:
new_list.append(ele)
count += 1
else:
new_list.append(ele)
return new_list
However, note that you can use use some built in functions to make your life much easier:
list.remove(x)
Remove the first item from the list whose value is equal to x. It raises a ValueError if there is no such item.
Therefore, you can more simply do:
def remove_first_two(list, n):
if list == []:
return []
for _ in range(2):
if n in list:
list.remove(n)
return list
Python updates the list if you change it while iterating.
In you test case with list = [1,2,2,3] when list[1] is deleted and Python updates list = [1,2,3]. Now Python understands you have iterated till index 1 and continues from index 2 which now contains 3. So Python encounters only one occurance of 2.
So heed your friends advice and do not edit list while iterating :)
Now you can use Python's in-built list.remove(element) to delete first ocuurence of a element. Repeat it 2 times for desired output.
Also O(n) with a single parse.
def remove_first_two(mylist,n):
counter = 0
def myfilter (i):
nonlocal counter,n
if counter > 2:
return True
else:
counter += 1
return (i != n)
return (list(filter(myfilter,mylist)))
This can also be done in python 3.8 using assignment expressions in a list comprehension:
data = [1,2,3,2,3,2,4]
count = 2
num = 2
[x for x in data if x != num or (count:=count-1) < 0]
Results:
[1, 3, 2, 2, 4]
Here is the reason why your program does not work:
When you remove an element, the for loop moves on to the next element, but by "moving on" it is actually skipping the element which now occupies the position of the deleted element. It skips the element right after the one you deleted.
The correct way to iterate over a list while you delete elements is making index progression explicit, by using a while loop instead of a for loop, and not increase the index when you delete an element:
i = 0
while i < len(my_list):
if condition:
my_list.pop(i)
else:
i += 1
However, none of this is necessary in your case! Notice that when you use my_list.remove(ele), you are not providing an index as you would with my_list.pop(i), so Python has to search for the first element that matches ele. Although remove will be slower than pop when used by themselves, here remove allows you not use any loops at all, simply do my_list.remove(n) twice!
Last touch: If your list has less than two elements matching n, one of the two my_list.remove(n) commands would return a ValueError. You can account for this exception, knowing that if it happens, your list is ready and requires no further action.
So the code you need is:
try:
my_list.remove(n)
my_list.remove(n)
except ValueError:
pass

recursively remove adjacent duplicates in a list

I looked up and found a close example, but the answer found in this link: Remove adjacent duplicate elements from a list won't run the test cases for this problem. So this is all I have so far:
def remove_dups(thelist):
"""Returns: a COPY of thelist with adjacent duplicates removed.
Example: for thelist = [1,2,2,3,3,3,4,5,1,1,1],
the answer is [1,2,3,4,5,1]
Precondition: thelist is a list of ints"""
i = 1
if len(thelist) == 0:
return []
elif len(thelist) == 1:
return thelist
elif thelist[i] == thelist[i-1]:
del thelist[i]
return remove_dups(thelist[i:])
def test_remove_dups():
assert_equals([], remove_dups([]))
assert_equals([3], remove_dups([3,3]))
assert_equals([4], remove_dups([4]))
assert_equals([5], remove_dups([5, 5]))
assert_equals([1,2,3,4,5,1], remove_dups([1,2,2,3,3,3,4,5,1,1,1]))
# test for whether the code is really returning a copy of the original list
mylist = [3]
assert_equals(False, mylist is remove_dups(mylist))
EDIT while I do understand that the accepted answer linked above using itertools.groupby would work, I think it wouldn't teach me what's wrong with my code & and would defeat the purpose of the exercise if I imported grouby from itertools.
from itertools import groupby
def remove_dups(lst):
return [k for k,items in groupby(lst)]
If you really want a recursive solution, I would suggest something like
def remove_dups(lst):
if lst:
firstval = lst[0]
# find lowest index of val != firstval
for index, value in enumerate(lst):
if value != firstval:
return [firstval] + remove_dups(lst[index:])
# no such value found
return [firstval]
else:
# empty list
return []
Your assertion fails, because in
return thelist
you are returning the same list, and not a copy as specified in the comments.
Try:
return thelist[:]
When using recursion with list it is most of the time a problem of returning a sub-list or part of that list. Which makes the termination case testing for an empty list. And then you have the two cases:
The current value is different from the last one we saw so we want to keep it
The current value is the same as the last one we saw so we discard it and keep iterating on the "rest" of the values.
Which translate in this code:
l = [1,2,2,3,3,3,4,5,1,1,1]
def dedup(values, uniq):
# The list of values is empty our work here is done
if not values:
return uniq
# We add a value in 'uniq' for two reasons:
# 1/ it is empty and we need to start somewhere
# 2/ it is different from the last value that was added
if not uniq or values[0] != uniq[-1]:
uniq.append(values.pop(0))
return dedup(values, uniq)
# We just added the exact same value so we remove it from 'values' and
# move to the next iteration
return dedup(values[1:], uniq)
print dedup(l, []) # output: [1, 2, 3, 4, 5, 1]
problem is with your return statement,
you are returning
return remove_dups(thelist[i:])
output will be always last n single element of list
like for above soon,
print remove_dups([1,2,2,3,3,3,4,5,1,1,1])
>>> [1] #as your desired is [1,2,3,4,5,1]
which returns finally a list of single element as it don't consider Oth element.
here is recursive solution.
def remove_dups(lst):
if len(lst)>1:
if lst[0] != lst[1]:
return [lst[0]] + remove_dups(lst[1:])
del lst[1]
return remove_dups(lst)
else:
return lst

Search function python

Hi Im trying to create a search function in python, that goes through a list and searches for an element in it.
so far ive got
def search_func(list, x)
if list < 0:
return("failure")
else:
x = list[0]
while x > list:
x = list [0] + 1 <---- how would you tell python to go to the next element in the list ?
if (x = TargetValue):
return "success"
else
return "failure"
Well, you current code isn't very Pythonic. And there are several mistakes! you have to use indexes to acces an element in a list, correcting your code it looks like this:
def search_func(lst, x):
if len(lst) <= 0: # this is how you test if the list is empty
return "failure"
i = 0 # we'll use this as index to traverse the list
while i < len(lst): # this is how you test to see if the index is valid
if lst[i] == x: # this is how you check the current element
return "success"
i += 1 # this is how you advance to the next element
else: # this executes only if the loop didn't find the element
return "failure"
... But notice that in Python you rarely use while to traverse a list, a much more natural and simpler approach is to use for, which automatically binds a variable to each element, without having to use indexes:
def search_func(lst, x):
if not lst: # shorter way to test if the list is empty
return "failure"
for e in lst: # look how easy is to traverse the list!
if e == x: # we no longer care about indexes
return "success"
else:
return "failure"
But we can be even more Pythonic! the functionality you want to implement is so common that's already built into lists. Just use in to test if an element is inside a list:
def search_func(lst, x):
if lst and x in lst: # test for emptiness and for membership
return "success"
else:
return "failure"
Are you saying you want to see if an element is in a list? If so, there is no need for a function like that. Just use in:
>>> lst = [1, 2, 3]
>>> 1 in lst
True
>>> 4 in lst
False
>>>
This method is a lot more efficient.
If you have to do it without in, I suppose this will work:
def search_func(lst, x):
return "success" if lst.count(x) else "failure"
you dont need to write a function for searching, just use
x in llist
Update:
def search_func(llist,x):
for i in llist:
if i==x:
return True
return False
You are making your problem more complex, while solving any problem just think before starting to code. You are using while loops and so on which may sometimes becomes an infinite loop. You should use a for loop to solve it. This is better than while loop. So just check which condition helps you. That's it you are almost done.
def search_func(lst,x):
for e in lst: #here e defines elements in the given list
if e==x: #if condition checks whether element is equal to x
return True
else:
return False
def search(query, result_set):
if isinstance(query, str):
query = query.split()
assert isinstance(query, list)
results = []
for i in result_set:
if all(quer.casefold() in str(i).casefold() for quer in query):
results.append(i)
return results
Works best.

Searching values of a list in another List using Python

I'm a trying to find a sublist of a list. Meaning if list1 say [1,5] is in list2 say [1,4,3,5,6] than it should return True. What I have so far is this:
for nums in l1:
if nums in l2:
return True
else:
return False
This would be true but I'm trying to return True only if list1 is in list2 in the respective order. So if list2 is [5,2,3,4,1], it should return False. I was thinking along the lines of comparing the index values of list1 using < but I'm not sure.
try:
last_found = -1
for num in L1:
last_found = L2.index(num, last_found + 1)
return True
except ValueError:
return False
The index method of list L2 returns the position at which the first argument (num) is found in the list; called, like here, with a second arg, it starts looking in the list at that position. If index does not find what it's looking for, it raises a ValueError exception.
So, this code uses this approach to look for each item num of L1, in order, inside L2. The first time it needs to start looking from position 0; each following time, it needs to start looking from the position just after the last one where it found the previous item, i.e. last_found + 1 (so at the start we must set last_found = -1 to start looking from position 0 the first time).
If every item in L1 is found this way (i.e. it's found in L2 after the position where the previous item was found), then the two lists meet the given condition and the code returns True. If any item of L1 is ever not-found, the code catches the resulting ValueError exception and just returns False.
A different approach would be to use iterators over the two lists, that can be formed with the iter built-in function. You can "advance" an iterator by calling built-in next on it; this will raise StopIteration if there is no "next item", i.e., the iterator is exhausted. You can also use for on the iterator for a somewhat smoother interface, where applicable. The low-level approach using the iter/next idea:
i1 = iter(L1)
i2 = iter(L2)
while True:
try:
lookfor = next(i1)
except StopIteration:
# no more items to look for == all good!
return True
while True:
try:
maybe = next(i2)
except StopIteration:
# item lookfor never matched == nope!
return False
if maybe == lookfor:
break
or, a bit higher-level:
i1 = iter(L1)
i2 = iter(L2)
for lookfor in i1:
for maybe in i2:
if maybe == lookfor:
break
else:
# item lookfor never matched == nope!
return False
# no more items to look for == all good!
return True
In fact, the only crucial use of iter here is to get i2 -- having the inner loop as for maybe in i2 guarantees the inner loop won't start looking from the beginning every time, but, rather, it will keep looking where it last left off. The outer loop might as well for for lookfor in L1:, since it has no "restarting" issue.
Key, here, is the else: clause of loops, which triggers if, and only if, the loop was not interrupted by break but rather exited naturally.
Working further on this idea we are again reminded of the in operator, which also can be made to continue where it last left off simply by using an iterator. Big simplification:
i2 = iter(L2)
for lookfor in L1:
if lookfor not in i2:
return False
# no more items to look for == all good!
return True
But now we recognize that is exactly the patter abstracted by the short-circuiting any and all built-in "short-circuiting accumulator" functions, so...:
i2 = iter(L2)
return all(lookfor in i2 for lookfor in L1)
which I believe is just about as simple as you can get. The only non-elementary bit left here is: you need to use an iter(L2) explicitly, just once, to make sure the in operator (intrinsically an inner loop) doesn't restart the search from the beginning but rather continues each time from where it last left off.
This question looks a bit like homework and for this reason I'd like to take the time and discuss what may be going wrong with the snippet shown in the question.
Although you are using a word in its plural form, for the nums variable, you need to understand that Python will use this variable to store ONE item from l1 at a time, and go through the block of code in this "for block", one time for each different item.
The result of your current snippet will therefore be to exit upon the very first iteration, with either True or False depending if by chance the first items in the list happen to match.
Edit: Yes, A1, exactly as you said: the logic exits with True after the first iteration. This is because of the "return" when nums is found in l2.
If you were to do nothing in the "found" case, the loop the logic would proceed with finishing whatever logic in the block (none here) and it would then start the next iteration. Therefore it would only exit with a "False" return value, in the case when an item from l1 is not found l2 (indeed after the very first such not-found item). Therefore your logic is almost correct (if it were to do nothing in the "found case"), the one thing missing would be to return "True", systematically after the for loop (since if it didn't exit with a False value within the loop, then all items of l2 are in l1...).
There are two ways to modify the code so it does nothing for the "found case".
- by using pass, which is a convenient way to instruct Python to do nothing; "pass" is typically used when "something", i.e. some action is syntactically required but we don't want anything done, but it can also be used when debugging etc.
- by rewriting the test as a "not in" instead
if nums not in l2:
return False
#no else:, i.e. do nothing at all if found
Now... Getting into more details.
There may be a flaw in your program (with the suggested changes), that is that it would consider l1 to be a sublist of l2, even if l1 had say 2 items with value say 5 whereby l2 only had one such value. I'm not sure if that kind of consideration is part of the problem (possibly the understanding is that both lists are "sets", with no possible duplicate items). If duplicates were allowed however, you would have to complicate the logic somewhat (a possible approach would be to intitially make a copy of l2 and each time "nums" is find in the l2 copy, to remove this item.
Another consideration is that maybe a list can only be said to be a sublist if its items are found the same order as the items in the other list... Again it all depends on the way the problem is defined... BTW some of the solutions proposed, like Alex Martelli's are written in such fashion because they solve the problem in a way that the order of items with the lists matter.
I think this solution is the fastest, since it iterates only once, albeit on the longer list and exits before finishing the iteration if a match is found. (Edit: However, it is not as succinct or as fast as Alex's latest solution)
def ck(l1,l2):
i,j = 0,len(l1)
for e in l2:
if e == l1[i]:
i += 1
if i == j:
return True
return False
An improvement was suggested by Anurag Uniyal (see comment) and is reflected in the showdown below.
Here are some speed results for a range of list size ratios (List l1 is a 10-element list containing random values from 1-10. List l2 ranges from 10-1000 in length (and also contain random values from 1-10).
Code that compares run times and plots the results:
import random
import os
import pylab
import timeit
def paul(l1,l2):
i = 0
j = len(l1)
try:
for e in l2:
if e == l1[i]:
i += 1
except IndexError: # thanks Anurag
return True
return False
def jed(list1, list2):
try:
for num in list1:
list2 = list2[list2.index(num):]
except: return False
else: return True
def alex(L1,L2): # wow!
i2 = iter(L2)
return all(lookfor in i2 for lookfor in L1)
from itertools import dropwhile
from operator import ne
from functools import partial
def thc4k_andrea(l1, l2):
it = iter(l2)
try:
for e in l1:
dropwhile(partial(ne, e), it).next()
return True
except StopIteration:
return False
ct = 100
ss = range(10,1000,100)
nms = 'paul alex jed thc4k_andrea'.split()
ls = dict.fromkeys(nms)
for nm in nms:
ls[nm] = []
setup = 'import test_sublist as x'
for s in ss:
l1 = [random.randint(1,10) for i in range(10)]
l2 = [random.randint(1,10) for i in range(s)]
for nm in nms:
stmt = 'x.'+nm+'(%s,%s)'%(str(l1),str(l2))
t = timeit.Timer(setup=setup, stmt=stmt).timeit(ct)
ls[nm].append( t )
pylab.clf()
for nm in nms:
print len(ss), len(ls[nm])
pylab.plot(ss,ls[nm],label=nm)
pylab.legend(loc=0)
pylab.xlabel('length of l2')
pylab.ylabel('time')
pylab.savefig('cmp_lsts.png')
os.startfile('cmp_lsts.png')
results:
This should be easy to understand and avoid corner case nicely as you don't need to work with indexes:
def compare(l1, l2):
it = iter(l2)
for e in l1:
try:
while it.next() != e: pass
except StopIteration: return False
return True
it tries to compare each element of l1 to the next element in l2.
if there is no next element (StopIteration) it returns false (it visited the whole l2 and didn't find the current e) else it found it, so it returns true.
For faster execution it may help to put the try block outside the for:
def compare(l1, l2):
it = iter(l2)
try:
for e in l1:
while it.next() != e: pass
except StopIteration: return False
return True
I have a hard time seeing questions like this and not wishing that Python's list handling was more like Haskell's. This seems a much more straightforward solution than anything I could come up with in Python:
contains_inorder :: Eq a => [a] -> [a] -> Bool
contains_inorder [] _ = True
contains_inorder _ [] = False
contains_inorder (x:xs) (y:ys) | x == y = contains_inorder xs ys
| otherwise = contains_inorder (x:xs) ys
The ultra-optimized version of Andrea's solution:
from itertools import dropwhile
from operator import ne
from functools import partial
def compare(l1, l2):
it = iter(l2)
try:
for e in l1:
dropwhile(partial(ne, e), it).next()
return True
except StopIteration:
return False
This can be written even more functional style:
def compare(l1,l2):
it = iter(l2)
# any( True for .. ) because any([0]) is False, which we don't want here
return all( any(True for _ in dropwhile(partial(ne, e), it)) for e in l1 )
I have a feeling this is more intensive than Alex's answer, but here was my first thought:
def test(list1, list2):
try:
for num in list1:
list2 = list2[list2.index(num):]
except: return False
else: return True
Edit: Just tried it. His is faster. It's close.
Edit 2: Moved try/except out of the loop (this is why others should look at your code). Thanks, gnibbler.

Categories

Resources