测试列表是否包含另一个Python列表

时间:2010-10-02 20:20:32

标签: python list contains list-comparison

如何测试列表是否包含另一个列表(即,它是一个连续的子序列)。假设有一个名为contains的函数:

contains([1,2], [-1, 0, 1, 2]) # Returns [2, 3] (contains returns [start, end])
contains([1,3], [-1, 0, 1, 2]) # Returns False
contains([1, 2], [[1, 2], 3]) # Returns False
contains([[1, 2]], [[1, 2], 3]) # Returns [0, 0]

编辑:

contains([2, 1], [-1, 0, 1, 2]) # Returns False
contains([-1, 1, 2], [-1, 0, 1, 2]) # Returns False
contains([0, 1, 2], [-1, 0, 1, 2]) # Returns [1, 3]

17 个答案:

答案 0 :(得分:44)

如果所有项目都是唯一的,则可以使用集合。

>>> items = set([-1, 0, 1, 2])
>>> set([1, 2]).issubset(items)
True
>>> set([1, 3]).issubset(items)
False

答案 1 :(得分:17)

这是我的版本:

def contains(small, big):
    for i in xrange(len(big)-len(small)+1):
        for j in xrange(len(small)):
            if big[i+j] != small[j]:
                break
        else:
            return i, i+len(small)
    return False

它返回了一个(start,end + 1)的元组,因为我认为这更像是pythonic,正如Andrew Jaffe在他的评论中指出的那样。它没有对任何子列表进行切片,因此应该合理有效。

新手的一个兴趣点是它使用else clause on the for statement - 这不是我经常使用的东西,但在这种情况下可能是非常宝贵的。

这与在字符串中查找子字符串相同,因此对于大型列表,实现Boyer-Moore algorithm之类的内容可能更有效。

答案 2 :(得分:11)

all()any()功能可以执行此操作。 检查list1是否包含list2中的所有元素

result = all(elem in list1 for elem in list2)

检查list1是否包含list2中的任何元素

result = any(elem in list1 for elem in list2)

变量result将是boolean(TRUE / FALSE)。

答案 3 :(得分:4)

如果big列表真的很大,我可以谦虚地建议Rabin-Karp algorithm。该链接甚至包含几乎可用的代码 - 几乎是Python。

答案 4 :(得分:3)

OP编辑后:

def contains(small, big):
    for i in xrange(1 + len(big) - len(small)):
        if small == big[i:i+len(small)]:
            return i, i + len(small) - 1
    return False

答案 5 :(得分:2)

这很有效,因为它使用内置list.index()方法和==运算符进行线性搜索:

def contains(sub, pri):
    M, N = len(pri), len(sub)
    i, LAST = 0, M-N+1
    while True:
        try:
            found = pri.index(sub[0], i, LAST) # find first elem in sub
        except ValueError:
            return False
        if pri[found:found+N] == sub:
            return [found, found+N-1]
        else:
            i = found+1

答案 6 :(得分:1)

如果我们改进问题,谈论测试列表是否包含另一个列表作为序列,答案可能是下一个单行:

def contains(subseq, inseq):
    return any(inseq[pos:pos + len(subseq)] == subseq for pos in range(0, len(inseq) - len(subseq) + 1))

这里我用来调整这个单行的单元测试:

https://gist.github.com/anonymous/6910a85b4978daee137f

答案 7 :(得分:1)

return this.token

它提供了真实的输出。

a=[[1,2] , [3,4] , [0,5,4]]
print(a.__contains__([0,5,4]))

它提供错误的输出。

答案 8 :(得分:1)

这是我的答案。此函数将帮助您确定B是否是A的子列表。时间复杂度为O(n)。

`def does_A_contain_B(A, B): #remember now A is the larger list
    b_size = len(B)
    for a_index in range(0, len(A)):
        if A[a_index : a_index+b_size]==B:
            return True
    else:
        return False`

答案 9 :(得分:1)

最小的代码:

def contains(a,b):
    str(a)[1:-1].find(str(b)[1:-1])>=0

答案 10 :(得分:1)

这是一个使用列表方法的简单算法:

#!/usr/bin/env python

def list_find(what, where):
    """Find `what` list in the `where` list.

    Return index in `where` where `what` starts
    or -1 if no such index.

    >>> f = list_find
    >>> f([2, 1], [-1, 0, 1, 2])
    -1
    >>> f([-1, 1, 2], [-1, 0, 1, 2])
    -1
    >>> f([0, 1, 2], [-1, 0, 1, 2])
    1
    >>> f([1,2], [-1, 0, 1, 2])
    2
    >>> f([1,3], [-1, 0, 1, 2])
    -1
    >>> f([1, 2], [[1, 2], 3])
    -1
    >>> f([[1, 2]], [[1, 2], 3])
    0
    """
    if not what: # empty list is always found
        return 0
    try:
        index = 0
        while True:
            index = where.index(what[0], index)
            if where[index:index+len(what)] == what:
                return index # found
            index += 1 # try next position
    except ValueError:
        return -1 # not found

def contains(what, where):
    """Return [start, end+1] if found else empty list."""
    i = list_find(what, where)
    return [i, i + len(what)] if i >= 0 else [] #NOTE: bool([]) == False

if __name__=="__main__":
    import doctest; doctest.testmod()

答案 11 :(得分:0)

我觉得这个很快......

def issublist(subList, myList, start=0):
    if not subList: return 0
    lenList, lensubList = len(myList), len(subList)
    try:
        while lenList - start >= lensubList:
            start = myList.index(subList[0], start)
            for i in xrange(lensubList):
                if myList[start+i] != subList[i]:
                    break
            else:
                return start, start + lensubList - 1
            start += 1
        return False
    except:
        return False

答案 12 :(得分:0)

大多数答案的问题是,它们对于列表中的唯一项很有用。如果项目不是唯一的,并且您仍然想知道是否有一个交叉点,则应该计算项目:

from collections import Counter as count

def listContains(l1, l2):
  list1 = count(l1)
  list2 = count(l2)

  return list1&list2 == list1

print( listContains([1,1,2,5], [1,2,3,5,1,2,1]) ) # Returns True
print( listContains([1,1,2,8], [1,2,3,5,1,2,1]) ) # Returns False

您还可以使用''.join(list1&list2)

返回交点

答案 13 :(得分:0)

我试图尽可能提高效率。

它使用发电机;建议那些不熟悉这些野兽的人查看their documentationyield expressions的内容。

基本上它从子序列创建一个值的生成器,可以通过发送一个真值来重置它。如果重置了发生器,它将从sub的开头再次开始产生。

然后它只是将sequence的连续值与生成器产量进行比较,如果它们不匹配则重置生成器。

当生成器耗尽值时,即在没有重置的情况下到达sub的末尾,这意味着我们找到了匹配。

由于它适用于任何序列,您甚至可以在字符串上使用它,在这种情况下它的行为与str.find类似,只是它返回False而不是-1

进一步说明:我认为返回元组的第二个值应符合Python标准,通常应高一个。即"string"[0:2] == "st"。但规范另有说法,这就是它的工作原理。

这取决于这是否是一个通用例程,或者它是否正在实现某个特定目标;在后一种情况下,最好实现一个通用例程,然后将其包装在一个函数中,该函数将返回值调整为适合规范。

def reiterator(sub):
    """Yield elements of a sequence, resetting if sent ``True``."""
    it = iter(sub)
    while True:
        if (yield it.next()):
            it = iter(sub)

def find_in_sequence(sub, sequence):
    """Find a subsequence in a sequence.

    >>> find_in_sequence([2, 1], [-1, 0, 1, 2])
    False
    >>> find_in_sequence([-1, 1, 2], [-1, 0, 1, 2])
    False
    >>> find_in_sequence([0, 1, 2], [-1, 0, 1, 2])
    (1, 3)
    >>> find_in_sequence("subsequence",
    ...                  "This sequence contains a subsequence.")
    (25, 35)
    >>> find_in_sequence("subsequence", "This one doesn't.")
    False

    """
    start = None
    sub_items = reiterator(sub)
    sub_item = sub_items.next()
    for index, item in enumerate(sequence):
        if item == sub_item:
            if start is None: start = index
        else:
            start = None
        try:
            sub_item = sub_items.send(start is None)
        except StopIteration:
            # If the subsequence is depleted, we win!
            return (start, index)
    return False

答案 14 :(得分:0)

这里的代码较少,而且易于理解(或者至少我喜欢这样认为)。

如果您要保持顺序(仅当较小的列表在较大的列表中以相同的顺序找到时才匹配):

def is_ordered_subset(l1, l2):
    # First check to see if all element of l1 are in l2 (without checking order)
    if not set(l1).issubset(l2): 
        return False

    length = len(l1)
    # Make sublist of same size than l1
    list_of_sublist = [l2[i:i+length] for i, x in enumerate(l2)]
    #Check if one of this sublist is l1
    return l1 in list_of_sublist 

答案 15 :(得分:0)

戴夫答案很好。但我建议这种实现方式效率更有效,并且不使用嵌套循环。

def contains(small_list, big_list):
    """
    Returns index of start of small_list in big_list if big_list
    contains small_list, otherwise -1.
    """
    loop = True
    i, curr_id_small= 0, 0
    while loop and i<len(big_list):
        if big_list[i]==small_list[curr_id_small]:
            if curr_id_small==len(small_list)-1:
                loop = False
            else:
                curr_id_small += 1
        else:
            curr_id_small = 0
        i=i+1
    if not loop:
        return i-len(small_list)
    else:
        return -1

答案 16 :(得分:0)

你可以使用 numpy:

def contains(l1, l2):
   """ returns True if l2 conatins l1 and False otherwise """

   if len(np.intersect1d(l1,l2))==len(l1):
      return = True
   else:
      return = False