I have two lists, A & B, and I would like to test whether A is contained in B. By "contained" I mean that the elements of A appear in the exact same order within B with no other elements between them. What I'm looking for is very similar to the behavior of A in B if they were strings.
Some elements of A will be repeated. We can assume A will be shorter than B.
There are many answers to similar questions on SO, but most answer a different question:
Is A an element of B? (Not my question: B is a flat list, not a list of lists.)
Are all the elements of A contained in B? (Not my question: I'm concerned about order as well.)
Is A a sublist of B? (Not my question: I don't want to know whether the elements of A appear in the same order in B, I want to know if they appear exactly as they are somewhere in B.)
If the operation were implemented as the keyword containedin, it would behave like this.
>>> [2, 3, 4] containedin [1, 2, 3, 4, 5]
True
>>> [2, 3, 4] containedin [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]
False
>>> [2, 3, 4] containedin [5, 4, 3, 2, 1]
False
>>> [2, 2, 2] containedin [1, 2, 3, 4, 5]
False
>>> [2, 2, 2] containedin [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]
False
>>> [2, 2, 2] containedin [1, 1, 1, 2, 2, 2, 3, 3, 3]
True
Is there a concise way to perform this operation in Python? Am I missing some important terminology that would have led me to the answer more quickly?
Use any with list slicing:
def contained_in(lst, sub):
n = len(sub)
return any(sub == lst[i:i+n] for i in range(len(lst)-n+1))
Or, use join to join both lists to strings and use in operator:
def contained_in(lst, sub):
return ','.join(map(str, sub)) in ','.join(map(str, lst))
Usage:
>>> contained_in([1, 2, 3, 4, 5], [2, 3, 4])
True
>>> contained_in([1, 2, 2, 4, 5], [2, 3, 4])
False
many people have posted their answers. but I want to post my efforts anyway ;)
this is my code:
def containedin(a,b):
for j in range(len(b)-len(a)+1):
if a==b[j:j+len(a)]:
return True
return False
print(containedin([2, 3, 4],[1, 2, 3, 4, 5]))
print(containedin([2, 3, 4],[1, 1, 2, 2, 3, 3, 4, 4, 5, 5]))
print(containedin([2, 3, 4],[5, 4, 3, 2, 1]))
print(containedin([2, 2, 2],[1, 2, 3, 4, 5]))
print(containedin([2, 2, 2],[1, 1, 1, 2, 2, 2, 3, 3, 3]))
this is the output:
True
False
False
False
True
Assuming a always shorter than b what you can do is as follows.
any(a == b[i:i+len(a)] for i in range(len(b)-len(a)+1))
Considering you need to preserve order:
def contains(sub_array, array):
for i in range(len(array)-len(sub_array)+1):
for j in range(len(sub_array)):
if array[i+j] != sub_array[j]:
break
else:
return i, i+len(sub_array)
return False
Use this function
I tried to not make it complex
def contains(list1,list2):
str1=""
for i in list1:
str1+=str(i)
str2=""
for j in list2:
str2+=str(j)
if str1 in str2:
return True
else:
return False
Hope it works :)
Something like this?
class myList(list):
def in_other(self, other_list):
for i in range(0, len(other_list)-len(self)):
if other_list[i:i+len(self)] == self:
return True
else:
continue
if __name__ == "__main__":
x = myList([1, 2, 3])
b = [0, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
print(x.in_other(b))
No need to slice for every element:
def contains(seq, sub):
sub_length = len(sub)
sub_first = sub[0]
return any(sub == seq[index:index+sub_length]
for index, element in enumerate(seq)
if element == sub_first)
Usage:
>>> seq = [1, 2, 3, 4, 5]
>>> sub = [2, 3, 4]
>>> contains(seq, sub)
True
You can concatenate the 2 lists into two different strings. Then, write a function to check if one string is in another.
def containedin(a, b):
if b in a:
return True
return False`
Related
If you have two lists,
a = [1, 2, 3, 4, 5]
b = [1, 3, 2, 4, 7]
how can you count the number of times elements at a certain position coincide? For example 1 and 4 in the above example would have 2 cases of elements coinciding.
sum(a_ == b_ for a_, b_ in zip(a, b))
zip can give you the elements that share a position, and you can use sum to count the number of times they match:
a = [1, 2, 3, 4, 5]
b = [1, 3, 2, 4, 7]
print(sum(x == y for x, y in zip(a, b))) # 2
You can use below code and you will get positions which coincide and get sum of them as well.
a = [1, 2, 3, 4, 5]
b = [1, 3, 2, 4, 7]
print(len([i for i,val in enumerate(zip(a,b)) if val[0]==val[1]]))
to get positions you can use
print([i for i,val in enumerate(zip(a,b)) if val[0]==val[1]])
one more version:
a = [1, 2, 3, 4, 5]
b = [1, 3, 2, 4, 7]
print(sum(a[i] == b[i] for i in range(len(a))))
How about this?
# lists:
a = [1, 2, 3, 4, 5]
b = [1, 3, 2, 4, 7]
# initialize variables:
number_of_collisions = 0
# iterate over each element:
for i in range(len(a)):
if a[i] == b[i]:
number_of_collisions += 1
print(number_of_collisions)
Say I have [1, 2, 3, 4, 5, 6]. And I want [3, 4, 5, 6].
Currently I'm doing:
l = [1, 2, 3, 4, 5, 6]
l[-(len(l) - n):]
I'm not familiar with Python style, but this seems pretty hack-y.
Yes, by slicing:
>>> n = 2
>>> l = [1, 2, 3, 4, 5, 6]
>>> l[n:]
[3, 4, 5, 6]
Read the tutorial here to have a further understanding on how to manipulate Python data structures that support slicing.
Get creative and put it in a function, as mentioned in the comments:
def slice_it(l, n):
return l[n:]
demo:
>>> slice_it(l, 2)
[3, 4, 5, 6]
And as a lambda, as shown in the comments:
sliced_list = lambda l, n: l[n:]
demo:
>>> sliced_list(l, 2)
[3, 4, 5, 6]
You can use positive int as slice lower bound:
l = [1, 2, 3, 4, 5, 6]
l[n:]
You can just l[n:], rather than using negative indexing with the full length of the list.
For example, if len(l) == 6 then:
l[2:] == l[-4:]
Keep in mind #idjaw has a much better answer,
You can also use list comprehensions, like so:
>>> l = [1, 2, 3, 4, 5, 6]
>>> n = 3
>>> l = [x + 1 for x in range(len(l)) if x >= n]
>>> l
[4, 5, 6]
I'm teaching myself Python ahead of starting a new job. Its a Django job, so I have to stick to 2.7. As such, I'm reading Beginning Python by Hetland and don't understand his example of using slices to replicate list.extend() functionality.
First, he shows the extend method by
a = [1, 2, 3]
b = [4, 5, 6]
a.extend(b)
produces [1, 2, 3, 4, 5, 6]
Next, he demonstrates extend by slicing via
a = [1, 2, 3]
b = [4, 5, 6]
a[len(a):] = b
which produces the exact same output as the first example.
How does this work? A has a length of 3, and the terminating slice index point is empty, signifying that it runs to the end of the list. How do the b values get added to a?
Python's slice-assignment syntax means "make this slice equal to this value, expanding or shrinking the list if necessary". To fully understand it you may want to try out some other slice values:
a = [1, 2, 3]
b = [4, 5, 6]
First, lets replace part of A with B:
a[1:2] = b
print(a) # prints [1, 4, 5, 6, 3]
Instead of replacing some values, you can add them by assigning to a zero-length slice:
a[1:1] = b
print(a) # prints [1, 4, 5, 6, 2, 3]
Any slice that is "out of bounds" instead simply addresses an empty area at one end of the list or the other (too large positive numbers will address the point just off the end while too large negative numbers will address the point just before the start):
a[200:300] = b
print(a) # prints [1, 2, 3, 4, 5, 6]
Your example code simply uses the most "accurate" out of bounds slice at the end of the list. I don't think that is code you'd use deliberately for extending, but it might be useful as an edge case that you don't need to handle with special logic.
It's simply an extension of normal indexing.
>>> L
[1, 2, 3, 4, 5]
>>> L[2] = 42
>>> L
[1, 2, 42, 4, 5]
The __setitem__() method detects that a slice is being used instead of a normal index and behaves appropriately.
a = [1, 2, 3]
b = [4, 5, 6]
a[len(a):] = b
means element in a from position len(a) are elements in b. Which means extending a with b.
For a demonstration, consider looking at a subclass of list:
from __future__ import print_function # so I can run on Py 3 and Py 2
class EdList(list):
def __setitem__(self,index,value):
print('setitem: index={}, value={}'.format(index,value))
list.__setitem__(self,index,value)
print(self)
def __setslice__(self,i,j,seq):
print('setslice: i:{}, j:{}, seq:{}'.format(i,j,seq))
self.__setitem__(slice(i,j),seq)
Running on Python 3:
>>> a=EdList(range(10))
>>> a[300000:]=[1,2,3]
setitem: index=slice(300000, None, None), value=[1, 2, 3]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3]
>>> a[1:1]=[4,5,6]
setitem: index=slice(1, 1, None), value=[4, 5, 6]
[0, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3]
Running on Python 2:
>>> a=EdList(range(10))
>>> a[300000:]=[1,2,3]
setslice: i:300000, j:9223372036854775807, seq:[1, 2, 3]
setitem: index=slice(300000, 9223372036854775807, None), value=[1, 2, 3]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3]
>>> a[1:1]=[4,5,6]
setslice: i:1, j:1, seq:[4, 5, 6]
setitem: index=slice(1, 1, None), value=[4, 5, 6]
[0, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3]
It is confusing when you are first learning it, but you will learn to love it I think.
What is the best way to do this:
>>> replace2([1, 2, 3, 4, 5, 6])
[2, 1, 4, 3, 6, 5]
def replace2inplace(lst):
lst[1::2], lst[::2] = lst[::2], lst[1::2]
This uses slice assignment and slice step sizes to swap every pair in the list around, in-place:
>>> somelst = [1, 2, 3, 4, 5, 6]
>>> replace2inplace(somelst)
>>> somelst
[2, 1, 4, 3, 6, 5]
Otherwise you could use some itertools tricks:
from itertools import izip, chain
def replace2copy(lst):
lst1, lst2 = tee(iter(lst), 2)
return list(chain.from_iterable(izip(lst[1::2], lst[::2])))
which gives:
>>> replace2([1, 2, 3, 4, 5, 6])
[2, 1, 4, 3, 6, 5]
with the list() call optional; if you only need to loop over the result the generator is enough:
from itertools import izip, chain, islice, tee
def replace2gen(lst):
lst1, lst2 = tee(iter(lst))
return chain.from_iterable(izip(islice(lst1, 1, None, 2), islice(lst2, None, None, 2)))
for i in replace2gen([1, 2, 3, 4, 5, 6]):
print i
where replace2gen() can take arbitrary iterators too.
Out-of-place version:
def replace2(lst):
return [x for pair in zip(lst[1::2], lst[::2]) for x in pair]
My choice:
x = range(1,7)
res = [e for e in itertools.chain(*zip(x[1::2],x[0::2]))]
>>> a = [1, 2, 3, 4, 5, 6]
>>> sum([[x+1,x] for x in a if x&1 == True],[])
[2, 1, 4, 3, 6, 5]
EDIT: Some further explanation was requested:
The code steps through each element in the list a and, if the element is odd (x&1 == True) it puts that element and the next element into a list in reverse order ([x+1,x]).
With out the sum(...,[]) function we would have
[[2, 1], [4, 3], [6, 5]]
The sum(...,[]) function removes the internal square brackets giving
[2, 1, 4, 3, 6, 5]
This can be done more generally by using the index of the list rather than its value:
>>> a = [1, 2, 3, 4, 5, 6]
>>> sum([[a[x],a[x-1]] for x in range(len(a)) if x&1 == True],[])
[2, 1, 4, 3, 6, 5]
However, this will remove the last element of the list if its length is not even.
Can't figure out how to do this in a pretty way :
I have a list of n elements,
I want to access every m elements of the list.
For example : [1, 2, 3, 4, 5] and m = 2 would give
[2, 4]
I can do it simply with a loop, but ins't there a more "pythonic" way?
Thanks by advance !
EDIT :
Seems like I forgot something.
I want, not only get those values but modify them.
I tried slicing a[::2] = 3, but it doesn't work. . .
I'm searching for something similar
Slicing syntax does this for you:
>>> my_list = range(10)
>>> my_list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> my_list[::2]
[0, 2, 4, 6, 8]
>>> my_list[1::2]
[1, 3, 5, 7, 9]
Here's a way to wrap a list to get the original assignment behavior you wanted, but I'm not sure I'd recommend it:
class AssignableSlice(list):
def __setitem__(self, i, v):
if isinstance(i, slice):
for ii in xrange(*i.indices(len(self))):
self[ii] = v
else:
super(AssignableSlice, self).__setitem__(i, v)
a = AssignableSlice(range(10))
print a
a[::2] = 3
print a
a[1::3] = 99
print a
produces:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[3, 1, 3, 3, 3, 5, 3, 7, 3, 9]
[3, 99, 3, 3, 99, 5, 3, 99, 3, 9]
Ned's answer shows how to use slices to access a portion of the list. You can also assign to a slice, but you need to assign a list to the slice, for example:
>>> my_list = range(5)
>>> my_list
[0, 1, 2, 3, 4]
>>> my_list[::2]
[0, 2, 4]
>>> my_list[::2] = [0, 0, 0]
>>> my_list
[0, 1, 0, 3, 0]
Note that when the step in your slice is anything besides the default of 1 the list that you assign needs to be the same length, however with a default step you can actually change the size of the list with slice assignment:
>>> my_list = range(5)
>>> my_list
[0, 1, 2, 3, 4]
>>> my_list[:1]
[0]
>>> my_list[:1] = [4, 3, 2] # replace the first item with 3 new items
>>> my_list
[4, 3, 2, 1, 2, 3, 4]
>>> my_list[2:5]
[2, 1, 2]
>>> my_list[2:5] = [] # remove the middle three items from the list
>>> my_list
[4, 3, 3, 4]
I juste found a way to do what I want using slicing.
The following :
candidates[::2] = [1] * len(candidates[::2])
will replace every 2 elements of candidates by 1