【问题标题】:Search in lists of lists by given index按给定索引在列表列表中搜索
【发布时间】:2009-07-20 21:34:26
【问题描述】:

我有一个包含两项的列表,需要搜索其中的内容。

如果列表是:

list = [['a','b'], ['a','c'], ['b','d']]

我可以通过以下方式轻松搜索一对

['a','b'] in list

现在,有没有办法查看我是否有一对字符串仅出现在第二个位置?我可以这样做:

for i in range (0, len(list)):
    if list[i][1]==search:
       found=1

但是没有for 循环有没有(更好的)方法?我不需要知道i 或在找到后继续循环。

【问题讨论】:

    标签: python list


    【解决方案1】:

    这是 Pythonic 的方法:

    data = [['a','b'], ['a','c'], ['b','d']]
    search = 'c'
    any(e[1] == search for e in data)
    

    或者...好吧,我不会声称这是“一种真正的 Pythonic 方式”,因为在某些时候它变得有点主观,什么是 Pythonic,什么不是,或者哪种方法是比另一个更 Pythonic。但是使用any() 绝对是比for 循环更典型的Python 风格,例如RichieHindle's answer,

    any 的实现当然有一个隐藏循环,虽然它一找到匹配就跳出循环。


    由于我很无聊,我制作了一个计时脚本来比较不同建议的性能,并根据需要修改其中的一些以使 API 相同。现在,我们应该记住,最快的并不总是最好的,而且速度绝对与 Pythonic 不同。话虽如此,结果是……奇怪。显然for 循环非常快,这不是我所期望的,所以我会在不理解它们为什么会以这种方式出现的情况下对它们持保留态度。

    无论如何,当我使用问题中定义的列表和三个子列表时,每个子列表有两个元素,从最快到最慢,我得到了这些结果:

    1. RichieHindle's answerfor 循环,以 0.22 μs 计时
    2. Terence Honles' first suggestion 创建一个列表,时间为 0.36 μs
    3. Pierre-Luc Bedard's answer (last code block),0.43 μs
    4. 基本上在 Markus's answer 和来自 the original questionfor 循环之间绑定,时间为 0.48 μs
    5. Coady's answer 使用 operator.itemgetter(),0.53 μs
    6. 在 0.67 微秒时,Alex Martelli's answerifilter()Anon's answer 之间的距离足够近,可以算作平局(Alex 的速度始终快约半微秒)
    7. jojo's answer,我的,Brandon E Taylor's(与我的相同)和使用any()Terence Honles' second suggestion 之间的另一个足够紧密的关系,都在 0.81-0.82 μs 内出现
    8. 然后user27221's answer 使用嵌套列表推导,时间为 0.95 μs

    显然,实际的计时在其他任何人的硬件上都没有意义,但它们之间的差异应该可以让我们了解不同方法的接近程度。

    当我使用更长的列表时,情况会发生一些变化。我从问题中的列表开始,包含三个子列表,并附加了另外 197 个子列表,总共有 200 个子列表,每个长度为 2。使用这个更长的列表,结果如下:

    1. RichieHindle's answer,与较短的列表相同 0.22 μs
    2. Coady's answer 使用 operator.itemgetter(),再次以 0.53 μs 进行
    3. Terence Honles' first suggestion 创建一个列表,时间为 0.36 μs
    4. Alex Martelli's answerifilter()Anon's answer 之间的另一个虚拟连接,时间为 0.67 μs
    5. 在我的答案Brandon E Taylor's 相同的方法和使用any()Terence Honles' second suggestion 之间再次非常接近,都在 0.81-0.82 微秒内出现

    这些是在列表扩展时保持其原始时间的那些。其余的,没有,是

    1. for 循环来自 the original question,时间为 1.24 μs
    2. Terence Honles' first suggestion 创建一个列表,时间为 7.49 μs
    3. Pierre-Luc Bedard's answer (last code block),8.12 μs
    4. Markus's answer,10.27 μs
    5. jojo's answer,19.87 μs
    6. 最后user27221's answer 使用嵌套列表推导,时间为 60.59 微秒

    【讨论】:

    • 这是一个很好的答案。谢谢。
    【解决方案2】:

    你总是会有一个循环 - 有人可能会出现一个聪明的单行代码,将循环隐藏在对 map() 或类似的调用中,但它总是会在那里。

    除非性能是主要因素,否则我的偏好始终是简洁的代码。

    这可能是您的代码的 Pythonic 版本:

    data = [['a','b'], ['a','c'], ['b','d']]
    search = 'c'
    for sublist in data:
        if sublist[1] == search:
            print "Found it!", sublist
            break
    # Prints: Found it! ['a', 'c']
    

    一旦找到匹配项,它就会跳出循环。

    (顺便说一下,['b''d'] 中有一个错字。)

    【讨论】:

    • 在我的python“职业生涯”中,我喜欢这种方法,因为它很容易阅读。不过,我可能会回来尝试其他的性能。我的清单在某一时刻变得相当大。有什么地方可以比较不同方法的性能吗?
    • 使用timeit模块对这种东西进行性能测试:docs.python.org/library/timeit.html
    • 如果我们不知道要搜索的字符是否在嵌套列表中的第 [0]、[1]、[2] 等位置怎么办?例如我们正在搜索 'b',使用此方法只会返回 ['a','b'] 而不是 ['b','d']。
    • @Chris-Jr:将if sublist[1] == search 替换为if search in sublist
    【解决方案3】:
    >>> the_list =[ ['a','b'], ['a','c'], ['b''d'] ]
    >>> any('c' == x[1] for x in the_list)
    True
    

    【讨论】:

      【解决方案4】:

      以上都不错

      但是你想保留结果吗?

      如果是这样……

      你可以使用以下

      result = [element for element in data if element[1] == search]
      

      然后一个简单的

      len(result)
      

      让您知道是否找到任何东西(现在您可以对结果进行处理)

      当然这不处理长度小于一的元素 (除非您知道它们总是大于 长度为 1,在这种情况下你应该使用元组吗? (元组是不可变的))

      如果您知道所有项目都是固定长度,您也可以这样做:

      any(second == search for _, second in data)
      

      或者对于 len(data[0]) == 4:

      any(second == search for _, second, _, _ in data)
      

      ...我建议使用

      for element in data:
         ...
      

      而不是

      for i in range(len(data)):
         ...
      

      (供将来使用,除非您想保存或使用“i”,只是让您知道 '0' 不是必需的,如果你开始,你只需要使用完整的语法 非零值)

      【讨论】:

        【解决方案5】:
        >>> my_list =[ ['a', 'b'], ['a', 'c'], ['b', 'd'] ]
        >>> 'd' in (x[1] for x in my_list)
        True
        

        编辑添加:

        David 使用 any 的答案和我使用 in 的答案都会在他们找到匹配项时结束,因为我们使用的是生成器表达式。这是一个使用无限生成器的测试来证明:

        def mygen():
            ''' Infinite generator '''
            while True:
                yield 'xxx'  # Just to include a non-match in the generator
                yield 'd'
        
        print 'd' in (x for x in mygen())     # True
        print any('d' == x for x in mygen())  # True
        # print 'q' in (x for x in mygen())     # Never ends if uncommented
        # print any('q' == x for x in mygen())  # Never ends if uncommented
        

        我只是喜欢简单地使用 in 而不是 ==any

        【讨论】:

        • 这就是它应该做的。
        【解决方案6】:

        怎么样:

        list =[ ['a','b'], ['a','c'], ['b','d'] ]
        search = 'b'
        
        filter(lambda x:x[1]==search,list)
        

        这将返回列表列表中的每个列表,其中第二个元素等于搜索。

        【讨论】:

          【解决方案7】:

          Markus 有一种方法可以避免使用 for 这个词——这是另一种方法,它应该有更好的性能长期 the_lists...:

          import itertools
          found = any(itertools.ifilter(lambda x:x[1]=='b', the_list)
          

          【讨论】:

          • 啊,很好,亚历克斯来了。 ;-) 显然,gen exp 使用了“for”这个词——但是如果我们允许这样做,将目标解释为避免使用标准 for 循环结构而不是“for”这个词本身,那么给出的所有答案如何比较性能?
          • @Anon,我现在没有时间运行通常的 -mtimeit 东西(OSCON 已开启,我很忙;-),但根据以前的经验,我知道 itertools 倾向于像涂了油的闪电一样表演。所有答案都保存了 Markus 在第一场比赛中的停赛,因此从这个意义上说,它们都同样快。
          【解决方案8】:

          使用 gen exp 没有错,但如果目标是内联循环...

          >>> import itertools, operator
          >>> 'b' in itertools.imap(operator.itemgetter(1), the_list)
          True
          

          应该也是最快的。

          【讨论】:

            【解决方案9】:

            k 个旧帖子,但没有人使用列表表达式来回答:P

            list =[ ['a','b'], ['a','c'], ['b','d'] ]
            Search = 'c'
            
            # return if it find in either item 0 or item 1
            print [x for x,y in list if x == Search or y == Search]
            
            # return if it find in item 1
            print [x for x,y in list if y == Search]
            

            【讨论】:

              【解决方案10】:
              >>> the_list =[ ['a','b'], ['a','c'], ['b','d'] ]
              >>> "b" in zip(*the_list)[1]
              True
              

              zip() 采用一组列表并按索引将元素组合在一起,有效地转置列表列表矩阵。星号获取the_list 的内容并将其作为参数发送到zip,因此您有效地分别传递了三个列表,这正是zip 想要的。剩下的就是检查"b"(或其他)是否在由具有您感兴趣的索引的元素组成的列表中。

              【讨论】:

                【解决方案11】:

                我正在寻找字典的深度查找,但没有找到。基于这篇文章,我能够创建以下内容。感谢并享受!

                def deapFind( theList, key, value ):
                    result = False
                    for x in theList:
                        if( value == x[key] ):
                            return True
                    return result
                
                theList = [{ "n": "aaa", "d": "bbb" }, { "n": "ccc", "d": "ddd" }]
                print 'Result: ' + str (deapFind( theList, 'n', 'aaa'))
                

                我使用 == 而不是 in 运算符,因为 in 对部分匹配返回 true。 IOW:在 n 键上搜索 aa 会返回 true。我认为这不是我们所希望的。

                HTH

                【讨论】:

                • 对于它的价值,我认为你根本不需要结果变量。只返回 false 和结束
                【解决方案12】:

                我认为使用嵌套列表推导是解决这个问题的最优雅的方法,因为中间结果是元素所在的位置。一个实现是:

                list =[ ['a','b'], ['a','c'], ['b','d'] ]
                search = 'c'
                any([ (list.index(x),x.index(y)) for x in list for y in x if y == search ] )
                

                【讨论】:

                  【解决方案13】:

                  下面给出了一种在列表中准确查找项目所在位置的简单方法。

                  for i in range (0,len(a)):
                  sublist=a[i]
                  for i in range(0,len(sublist)):
                      if search==sublist[i]:
                          print "found in sublist "+ "a"+str(i)
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2022-01-24
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-02-07
                    • 1970-01-01
                    • 2016-09-20
                    • 2018-01-29
                    相关资源
                    最近更新 更多