【问题标题】:Filter list of strings to not contain any of the string from another list as a substring过滤字符串列表以不包含来自另一个列表的任何字符串作为子字符串
【发布时间】:2018-01-14 19:33:50
【问题描述】:

我有以下代码来选择另一个列表中不包含的值。

import re
isbn  = ["1111","2222","3333","4444","5555"]
sku = ["k1 1111", "k2 2222", "k3 3333", "k4 4444", "k5 5555", "k6 6666", "k7 7777", "k8 8888" ,"k9 1111"]

for x in isbn:
    for i in sku:
        if x not in i:
            print (i)

预期的结果应该是这样的:

k6 6666
k7 7777
k8 8888

但我得到了所有不匹配的值。我怎样才能得到如上所示的预期结果。

【问题讨论】:

    标签: python string python-3.x list


    【解决方案1】:

    您应该在循环中使用any。事实上,您可以使用以下 list comprehension 来实现它:

    >>> list_1  = ["1111","2222","3333","4444","5555"]
    >>> list_2 = ["k1 1111", "k2 2222", "k3 3333", "k4 4444", "k5 5555", "k6 6666", "k7 7777", "k8 8888" ,"k9 1111"]
    
    >>> [x for x in list_2 if not any( y in x for y in list_1)]
    ['k6 6666', 'k7 7777', 'k8 8888']
    

    如果list_1 中的任何字符串作为list2 中的子字符串存在,则此处any 将返回True。一旦找到匹配项,它将使迭代短路(不检查其他匹配项),并将结果返回为True

    如果您对使用any 不感兴趣,您可能会得到与下面的for 循环相同的结果:

    for x in list_2:
        for y in list_1:
            if y in x:
                break
        else:
            print(x)
    

    这将打印您想要的输出:

    k6 6666
    k7 7777
    k8 8888
    

    【讨论】:

    • 在没有任何()的底部代码。为什么 else 内衬 for 不内衬 if。
    • @LandOwner 如果您的for 循环在没有执行break 的情况下完成迭代,则将执行else。如果您的if 条件返回Truebreak 将停止内部循环,在这种情况下else 将不会被执行。
    【解决方案2】:

    您需要测试isbn 中的所有 值,然后才能得出任何匹配结果。

    不是先循环isbn,而是循环sku并使用每个isbn值测试该值; any() function 让这一切变得更简单、更高效:

    for value in sku:
        if not any(i in value for i in isbn):
            print(value)
    

    更有效的方法仍然是拆分 ISBN 部分,并针对一组进行测试:

    isbn_set = set(isbn)
    for value in sku:
        isbn_part = value.partition(' ')[-1]  # everything after the first space
        if isbn_part not in isbn_set:
            print(value)
    

    这完全避免了循环isbn;设置成员资格测试需要 O(1) 恒定时间;对于 N skus 和 M ISBN 值,这会产生一个 O(N) 循环(与 any() 的 O(NM) 循环相比)。

    任一版本都可以转换为list comprehension 以生成匹配列表;首选设置版本则变为:

    isbn_set = set(isbn)
    not_matched = [value for value in sku if value.partition(' ')[-1] not in isbn_set]
    

    后者的演示:

    >>> isbn  = ["1111","2222","3333","4444","5555"]
    >>> sku = ["k1 1111", "k2 2222", "k3 3333", "k4 4444", "k5 5555", "k6 6666", "k7 7777", "k8 8888" ,"k9 1111"]
    >>> isbn_set = set(isbn)
    >>> [value for value in sku if value.partition(' ')[-1] not in isbn_set]
    ['k6 6666', 'k7 7777', 'k8 8888']
    

    【讨论】:

      【解决方案3】:

      如果您从集合中删除匹配项,那么剩下的集合就是您所追求的:

      代码:

      skus = set(sku)
      for x in isbn:
          skus -= {i for i in skus if x in i}
      

      测试代码:

      isbn = ["1111", "2222", "3333", "4444", "5555"]
      sku = ["k1 1111", "k2 2222", "k3 3333", "k4 4444", "k5 5555", "k6 6666",
             "k7 7777", "k8 8888", "k9 1111"]
      
      skus = set(sku)
      for x in isbn:
          skus -= {i for i in skus if x in i}
      print(skus)
      

      结果:

      {'k6 6666', 'k7 7777', 'k8 8888'}
      

      【讨论】:

      • 这不是最直观的路径,仍然是 O(NM) 解决方案。
      • 实际上,这比 O(NM) 更糟糕,因为您循环 N 个 skus 以获得 M 个 isbn 值,而减法是另一个循环最多 N 个值,所以 O(N**2M) ,比 OP 的更糟糕。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-12
      • 1970-01-01
      • 2020-07-31
      • 1970-01-01
      • 1970-01-01
      • 2020-08-09
      • 1970-01-01
      相关资源
      最近更新 更多