如何检查一个列表中的所有项是否都存在于另一个列表中?

33

我有两个列表,比如说

List1 = ['a','c','c']
List2 = ['x','b','a','x','c','y','c']

现在我想找出List1中的所有元素是否都存在于List2中。在这种情况下,它们确实都存在。我不能使用subset函数,因为列表中可能有重复的元素。我可以使用for循环来计算List1中每个项出现的次数,并查看它是否小于或等于List2中出现的次数。有更好的方法吗?
谢谢。
6个回答

51
当出现次数不重要时,您仍然可以使用子集功能,通过即时创建一个集合:
>>> list1 = ['a', 'c', 'c']
>>> list2 = ['x', 'b', 'a', 'x', 'c', 'y', 'c']
>>> set(list1).issubset(list2)
True

如果您需要检查第二个列表中每个元素至少出现在第一个列表中相同的次数,您可以利用Counter类型并定义自己的子集关系:
>>> from collections import Counter
>>> def counterSubset(list1, list2):
        c1, c2 = Counter(list1), Counter(list2)
        for k, n in c1.items():
            if n > c2[k]:
                return False
        return True
   
>>> counterSubset(list1, list2)
True
>>> counterSubset(list1 + ['a'], list2)
False
>>> counterSubset(list1 + ['z'], list2)
False

如果您已经有计数器(这可能是存储数据的一个有用替代方案),您也可以将其写成单行形式:
>>> all(n <= c2[k] for k, n in c1.items())
True

嗨,第二部分就是我想要做的。看起来这是唯一的方法。谢谢! - pogo
['s','e','r','','','','y']['','','','y'] 的情况下,它会失败吗? - SIslam
@SIslam 取决于顺序。第二个列表是前一个列表的子集,因此我的答案中的解决方案将正确识别它。 - poke
1
如果列表相同,这将返回False。然而,如果它们相等,set(list1)<= set(list2)将返回True。 - emilyk
你也可以使用 not c1 - c2 代替 all(n <= c2[k] for k, n in c1.items())。这种方式不太易读,但可能更快。 - Aran-Fey
1
@Aran-Fey 我认为一旦你考虑到Counter是多重集合,它就不难理解了。但我认为fferri的基于交集的答案更明显地表达了多重集合操作(我不确定为什么fferri复制了我的差异答案而不是他们自己更好的交集答案,来自重复的问题...但是在我的回答中,我给出了两个答案)。 - abarnert

7

请注意以下内容:

>>>listA = ['a', 'a', 'b','b','b','c']
>>>listB = ['b', 'a','a','b','c','d']
>>>all(item in listB for item in listA)
True

如果你像在英语中一样阅读“all”行,这不是错误的,但可能会误导,因为listA有第三个'b',而listB没有。
这也有同样的问题:
def list1InList2(list1, list2):
    for item in list1:
        if item not in list2:
            return False
    return True

请注意,以下内容无法正常工作:

>>>tupA = (1,2,3,4,5,6,7,8,9)
>>>tupB = (1,2,3,4,5,6,6,7,8,9)
>>>set(tupA) < set(TupB)
False

如果将元组转换为列表,它仍然无法工作。我不知道为什么字符串可以工作,但整数不行。
可以工作,但有相同的问题,即不能计算元素出现次数:
>>>set(tupA).issubset(set(tupB))
True

使用集合并不是一个全面的解决方案来匹配多个相同元素。但这里有一个简短的解决方案/调整,可以避免使用try/except,如下:
all(True if sequenceA.count(item) <= sequenceB.count(item) else False for item in sequenceA)

一个使用三元条件运算符包装列表推导式的内置函数。Python 真是太棒了!请注意,"<=" 应该不是 "=="。
使用此解决方案,序列 A 和 B 可以是元组、列表和其他具有“count”方法的“序列”。两个序列中的元素可以是大多数类型。我现在不会将其与字典一起使用,因此使用“sequence”而不是“iterable”。

1

使用 Counter 和内置的交集方法的解决方案(注意,- 是适当的多重集合差异,而不是元素逐个减法):

from collections import Counter

def is_subset(l1, l2):
    c1, c2 = Counter(l1), Counter(l2)
    return not c1 - c2

测试:

>>> List1 = ['a','c','c']
>>> List2 = ['x','b','a','x','c','y','c']
>>> is_subset(List1, List2)
True

1
我不能使用子集函数,因为列表中可能会有重复的元素。
这意味着您希望将列表视为 多重集合 而不是 集合。在 Python 中处理多重集合的常规方法是使用 collections.CounterCounter 是用于计算可哈希对象的字典子类。它是一个无序集合,其中元素存储为字典键,它们的计数存储为字典值。计数允许是任何整数值,包括零或负计数。 Counter 类类似于其他语言中的袋或多重集合。 并且,虽然你可以通过循环和比较计数来实现多重集的子集(使用Counter实现),就像poke's answer中所述,但这是不必要的 - 就像你可以通过循环和测试in来实现集合的子集(使用setfrozenset实现),但也是不必要的。 Counter类型已经以显然的方式扩展了所有集合运算符用于多重集。<1因此,您可以仅使用这些运算符编写子集代码,并且它将适用于setCounter
使用(多)集差异:2
def is_subset(c1, c2):
    return not c1 - c2

或者使用(多重)集合交集:
def is_subset(c1, c2):
    def c1 & c2 == c1

1. 如果 Counter 实现了集合操作符,那么为什么它不直接实现真子集和子集的 <<= 呢?虽然我找不到邮件记录,但我很确定这个问题已经讨论过了,答案是“集合操作符”被定义为最初版本的 collections.abc.Set 中定义的特定一组运算符(自那以后已经扩展了,如果我没记错的话...),而不是所有 set 为方便起见包含的运算符,就像 Counter 没有像 intersection 这样的命名方法,友好地对待其他类型,而不仅仅是 &,就像 set 一样。

2. 这取决于Python中集合为空时期望为falsey,反之为truthy的事实。这里记录了内置类型的情况,bool测试回退到len的事实在这里解释了——但这最终只是一种约定,因此像numpy数组这样的“准集合”可以违反它,如果它们有充分的理由。对于像CounterOrderedDict等“真实集合”,它仍然适用。如果您真的担心这个问题,可以编写len(c1 - c2) == 0,但请注意,这与PEP 8的精神不符。


这是最佳答案。它使得比较这两个列表变得非常简单,谢谢! - EnterPassword

0

如果List1中的所有项都在List2中,则返回true

def list1InList2(list1, list2):
    for item in list1:
        if item not in list2:
            return False
    return True

1
我不是那个给你点踩的人,但你应该看一下PEP 8。你的if语法有误,有一个不必要的分号,而且你的变量命名风格应该保留给类。 - David Cain
1
此外,这可以简化为一行代码:all(item in List2 for item in List1) - David Cain
我使用List1和List2作为变量名,因为这是问题中提到的,而且我已经有一段时间没有使用Python了,所以我承认if周围的分号和括号是我的错误。 - jeffam217
如果您意识到您的答案事实上是错误的,明智的做法是编辑它以纠正错误(为了安全起见,请在解释器中验证)-或者您可以删除自己的答案。积极地对明显不正确的答案进行负面评价以防止其他用户浪费时间是一个常见的主题。将评论视为改进的动力。始终看到光明的一面!...并且:欢迎来到SO! - cfi
谢谢,正如您可能已经注意到的那样,我是这个网站的新手,所以我可能会犯一些错误,比如没有编辑(希望不会超过一次)。 - jeffam217

-1
def check_subset(list1, list2):
    try:
        [list2.remove(x) for x in list1]
        return 'all elements in list1 are in list2'
    except:
        return 'some elements in list1 are not in list2'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接