【问题标题】:Find the index of a dict within a list, by matching the dict's value通过匹配字典的值,在列表中查找字典的索引
【发布时间】:2010-12-08 20:00:35
【问题描述】:

我有一个字典列表:

list = [{'id':'1234','name':'Jason'},
        {'id':'2345','name':'Tom'},
        {'id':'3456','name':'Art'}]

如何通过匹配 name = 'Tom' 有效地找到索引位置 [0]、[1] 或 [2]?

如果这是一个一维列表,我可以执行 list.index() 但我不确定如何通过搜索列表中的字典值来继续。

【问题讨论】:

  • "list" 是列表构造函数,您最好为列表选择另一个名称(即使在示例中)。如果没有找到元素,应该是什么响应?引发异常?返回无?
  • 如果你需要这个很多,使用更合适的数据结构(也许{ 'Jason': {'id': '1234'}, 'Tom': {'id': '1245'}, ...}?)
  • @delnan 因为那是灾难的根源!如果有的话,应该是{'1234': {'name': 'Jason'}, ...}。并不是说这会对这个用例有所帮助。

标签: python


【解决方案1】:
lst = [{'id':'1234','name':'Jason'}, {'id':'2345','name':'Tom'}, {'id':'3456','name':'Art'}]

tom_index = next((index for (index, d) in enumerate(lst) if d["name"] == "Tom"), None)
# 1

如果您需要从名称中重复获取,您应该按名称索引它们(使用字典),这样 get 操作将是 O(1) 时间。一个想法:

def build_dict(seq, key):
    return dict((d[key], dict(d, index=index)) for (index, d) in enumerate(seq))

people_by_name = build_dict(lst, key="name")
tom_info = people_by_name.get("Tom")
# {'index': 1, 'id': '2345', 'name': 'Tom'}

【讨论】:

  • 恕我直言,这不是可读的,或者 Pythonic 是@Emile 的答案。因为目的并不是真正创建一个生成器(并且为此使用 next() 对我来说似乎很奇怪),所以目的只是获取索引。此外,这会引发 StopIteration,而 Python lst.index() 方法会引发 ValueError。
  • @benhoyt:我也不喜欢 StopIteration 异常,但是虽然您可以更改 next() 的默认值,但它引发的异常是固定的。 pythonicity 有点主观,所以我不会质疑它,可能 for 循环更 pythonic。另一方面,有些人将 next() 别名为 first(),这听起来肯定更好:first(index for (index, d) in ...)。
  • first() 听起来更好。您总是可以尝试/排除 StopIteration 并引发 ValueError 以便调用者具有一致性。或者将 next() 的默认值设置为 -1。
  • @gdw2:这样做时我得到SyntaxError: Generator expression must be parenthesized if not sole argument
  • @avoliva 在 next 周围添加一个括号,如下所示next((index for (index, d) in enumerate(lst) if d["name"] == "Tom"), None)
【解决方案2】:

一个简单易读的版本是

def find(lst, key, value):
    for i, dic in enumerate(lst):
        if dic[key] == value:
            return i
    return -1

【讨论】:

  • 这似乎是最具可读性和 Pythonic 的。它还很好地模仿了str.find() 的行为。您也可以将其命名为 index() 并引发 ValueError 而不是返回 -1,如果这样更可取的话。
  • 同意 - 通过在未找到匹配项时返回 -1,您将始终获得列表中的最后一个 dict,这可能不是您想要的。最好返回 None 并检查调用代码中是否存在匹配项。
【解决方案3】:

它效率不高,因为您需要遍历列表检查其中的每个项目 (O(n))。如果你想要效率,你可以使用dict of dicts。 关于这个问题,这是找到它的一种可能方法(不过,如果您想坚持这种数据结构,实际上 使用生成器 更有效,就像 Brent Newey 在 cmets 中所写的那样;另请参阅托克兰的回答):

>>> L = [{'id':'1234','name':'Jason'},
...         {'id':'2345','name':'Tom'},
...         {'id':'3456','name':'Art'}]
>>> [i for i,_ in enumerate(L) if _['name'] == 'Tom'][0]
1

【讨论】:

  • 您可以通过使用发电机获得您想要的效率。请参阅 tokland 的答案。
  • @Brent Newey:生成器不会改变这样一个事实,即您必须遍历整个列表,使得搜索 O(n) 正如aeter 声称的那样......取决于该列表的长度,使用生成器与使用 for 循环之间的差异或任何可能可以忽略不计的东西,而使用 dict 与使用列表之间的差异可能不会
  • @Brent:你是对的,但是如果搜索的项目在列表的末尾,它是否可以击败字典中的 O(1) 查找?
  • @Dirk 生成器的 next() 调用在找到匹配项时停止,因此它不必遍历整个列表。
  • @aeter 你说得很对。我指的是找到匹配项时能够停止。
【解决方案4】:

使用过滤器/索引组合似乎最合乎逻辑:

names=[{}, {'name': 'Tom'},{'name': 'Tony'}]
names.index(next(filter(lambda n: n.get('name') == 'Tom', names)))
1

如果您认为可能有多个匹配项:

[names.index(item) for item in filter(lambda n: n.get('name') == 'Tom', names)]
[1]

【讨论】:

    【解决方案5】:

    @faham 提供的答案是一个不错的单行,但它不会将索引返回到包含该值的字典。相反,它返回字典本身。这是一种简单的获取方法:如果索引不止一个,则为一个或多个索引的列表,如果没有,则为空列表:

    list = [{'id':'1234','name':'Jason'},
            {'id':'2345','name':'Tom'},
            {'id':'3456','name':'Art'}]
    
    [i for i, d in enumerate(list) if 'Tom' in d.values()]
    

    输出:

    >>> [1]
    

    我喜欢这种方法的地方在于,通过简单的编辑,您可以获得索引和字典作为元组的列表。这是我需要解决的问题并找到了这些答案。在下文中,我在不同的字典中添加了一个重复值以显示它是如何工作的:

    list = [{'id':'1234','name':'Jason'},
            {'id':'2345','name':'Tom'},
            {'id':'3456','name':'Art'},
            {'id':'4567','name':'Tom'}]
    
    [(i, d) for i, d in enumerate(list) if 'Tom' in d.values()]
    

    输出:

    >>> [(1, {'id': '2345', 'name': 'Tom'}), (3, {'id': '4567', 'name': 'Tom'})]
    

    此解决方案查找在其任何值中包含“Tom”的所有字典。

    【讨论】:

      【解决方案6】:

      这是一个查找字典索引位置(如果存在)的函数。

      dicts = [{'id':'1234','name':'Jason'},
               {'id':'2345','name':'Tom'},
               {'id':'3456','name':'Art'}]
      
      def find_index(dicts, key, value):
          class Null: pass
          for i, d in enumerate(dicts):
              if d.get(key, Null) == value:
                  return i
          else:
              raise ValueError('no dict with the key and value combination found')
      
      print find_index(dicts, 'name', 'Tom')
      # 1
      find_index(dicts, 'name', 'Ensnare')
      # ValueError: no dict with the key and value combination found
      

      【讨论】:

        【解决方案7】:

        我需要一个更通用的解决方案来解决列表中多个字典具有键值的可能性,以及使用列表理解的简单实现:

        dict_indices = [i for i, d in enumerate(dict_list) if d[dict_key] == key_value] 
        

        【讨论】:

          【解决方案8】:

          一个班轮!?

          elm = ([i for i in mylist if i['name'] == 'Tom'] or [None])[0]
          

          【讨论】:

            【解决方案9】:
            def search(itemID,list):
                 return[i for i in list if i.itemID==itemID]
            

            【讨论】:

              【解决方案10】:

              以下将返回第一个匹配项的索引:

              ['Tom' in i['name'] for i in list].index(True)
              

              【讨论】:

                【解决方案11】:

                我的答案最好用一本字典来使用

                food_time_dict = {"Lina": 312400, "Tom": 360054, "Den": 245800}
                print(list(food_time_dict.keys()).index("Lina"))
                

                我从字典中请求键,然后我翻译列表,如果没有添加,会出现错误然后我将它用作列表。但在你的代码上:

                lists = [{'id': '1234', 'name': 'Jason'},
                         {'id': '2345', 'name': 'Tom'},
                         {'id': '3456', 'name': 'Art'}]
                    
                    
                def dict_in_lists_index(lists, search):  # function for convenience
                    j = 0  # [j][i]
                    for i in lists:
                        try:  # try our varible search if not found in list
                            return f"[{j}][{list(i.values()).index(search)}]"
                            # small decor
                        except ValueError: # error was ValueError
                            pass # aa... what must was what you want to do
                        j += 1 # not found? ok j++
                    return "Not Found"
                    
                    
                def dict_cropped_index(lists, search):
                    for i in lists:
                        try:
                            return list(i.values()).index(search)
                        except ValueError:
                            pass
                    return "Not Found"
                    
                    
                print(dict_in_lists_index(lists, 'Tom')) # and end
                print(dict_cropped_index(lists, 'Tom')) # now for sure end
                

                【讨论】:

                  【解决方案12】:

                  对于给定的可迭代对象,more_itertools.locate 产生满足谓词的项的位置。

                  import more_itertools as mit
                  
                  
                  iterable = [
                      {"id": "1234", "name": "Jason"},
                      {"id": "2345", "name": "Tom"},
                      {"id": "3456", "name": "Art"}
                  ]
                  
                  list(mit.locate(iterable, pred=lambda d: d["name"] == "Tom"))
                  # [1]
                  

                  more_itertools 是一个第三方库,它实现了itertools recipes 以及其他有用的工具。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2021-02-27
                    • 2014-09-17
                    • 2015-07-03
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-11-22
                    • 1970-01-01
                    相关资源
                    最近更新 更多