【问题标题】:如何按字典的值对字典列表进行排序?
【发布时间】:2010-09-09 12:43:13
【问题描述】:

我有一个字典列表,并希望每个项目都按特定值排序。

考虑列表:

[{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

当按name排序时,应该变成:

[{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}]

【问题讨论】:

  • 阅读答案并关注operator.itemgetter。我可以在同一个过程中对多个值进行排序吗(例如我们有 [{'name':'Bart', 'age':10, 'note':3},{'name':'Homer','age':10,'note':2},{'name':'Vasile','age':20,'note':3}] 并使用:from operator import itemgetter newlist = sorted(old_list, key=itemgetter(-'note','name') 编辑:经过测试,它正在工作,但我不知道如何记下 DESC 并命名 ASC。跨度>

标签: python list sorting dictionary data-structures


【解决方案1】:

您必须实现自己的比较函数,该函数将按名称键的值比较字典。见Sorting Mini-HOW TO from PythonInfo Wiki

【讨论】:

  • 这太依赖链接了。你能提供更完整的答案吗?
  • 其他贡献者也已经提供了正确的答案。随意保留链接或删除答案。
【解决方案2】:

我猜你的意思是:

[{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

这将是这样排序的:

sorted(l,cmp=lambda x,y: cmp(x['name'],y['name']))

【讨论】:

    【解决方案3】:
    my_list = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
    
    my_list.sort(lambda x,y : cmp(x['name'], y['name']))
    

    my_list 现在将是您想要的。

    或者更好:

    从 Python 2.4 开始,有一个 key 参数更加高效和整洁:

    my_list = sorted(my_list, key=lambda k: k['name'])
    

    ...IMO,lambda 比 operator.itemgetter 更容易理解,但您的里程可能会有所不同。

    【讨论】:

    • 如果键未知并且不断变化,该怎么办?我的意思是只有一个键和值的字典列表,但键和值在不断变化时无法定义。
    • 我需要更多的例子来看看。尝试在 codereview stackexchange 上提交一个可能的解决方案,并询问是否有更好的方法。
    • @Sam如果要按dict中单个key的值排序,即使不知道key也可以key=lambda k: list(k.values())[0]
    【解决方案4】:

    sorted() 函数采用key= 参数

    newlist = sorted(list_to_be_sorted, key=lambda d: d['name']) 
    

    或者,您可以使用operator.itemgetter 而不是自己定义函数

    from operator import itemgetter
    newlist = sorted(list_to_be_sorted, key=itemgetter('name')) 
    

    为了完整起见,添加reverse=True以降序排序

    newlist = sorted(list_to_be_sorted, key=itemgetter('name'), reverse=True)
    

    【讨论】:

    • 使用key不仅更干净,而且更高效。
    • 最快的方法是添加一个 newlist.reverse() 语句。否则你可以定义一个比较像 cmp=lambda x,y: - cmp(x['name'],y['name'])。
    • 如果排序值是一个数字,你可以说: lambda k: (k['age'] * -1) 以获得反向排序
    • 这也适用于元组列表,如果您使用itemgetter(i),其中i 是要排序的元组元素的索引。
    • itemgetter 接受多个参数:itemgetter(1,2,3) 是一个返回像 obj[1], obj[2], obj[3] 这样的元组的函数,因此您可以使用它进行复杂的排序。
    【解决方案5】:
    import operator
    a_list_of_dicts.sort(key=operator.itemgetter('name'))
    

    'key' 用于按任意值排序,'itemgetter' 将该值设置为每个项目的 'name' 属性。

    【讨论】:

      【解决方案6】:

      您可以使用自定义比较函数,也可以传入一个计算自定义排序键的函数。这通常更有效,因为每个项目只计算一次键,而比较函数会被调用更多次。

      你可以这样做:

      def mykey(adict): return adict['name']
      x = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10}]
      sorted(x, key=mykey)
      

      但标准库包含一个用于获取任意对象项的通用例程:itemgetter。所以试试这个:

      from operator import itemgetter
      x = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10}]
      sorted(x, key=itemgetter('name'))
      

      【讨论】:

        【解决方案7】:
        import operator
        

        按 key='name' 对字典列表进行排序:

        list_of_dicts.sort(key=operator.itemgetter('name'))
        

        按 key='age' 对字典列表进行排序:

        list_of_dicts.sort(key=operator.itemgetter('age'))
        

        【讨论】:

        • 无论如何要结合姓名和年龄? (就像在 SQL ORDER BY name,age 中一样?)
        • @monojohnny:是的,只要让键返回一个元组,key=lambda k: (k['name'], k['age'])。 (或key=itemgetter('name', 'age'))。元组的cmp 将依次比较每个元素。真是太棒了。
        • 在文档 (docs.python.org/2/tutorial/datastructures.html) 中没有描述 list.sort() 的可选 key 参数。知道在哪里可以找到吗?
        • @TTT:请参阅 library documentation 获取 list 和朋友。
        【解决方案8】:

        如果要按多个键对列表进行排序,可以执行以下操作:

        my_list = [{'name':'Homer', 'age':39}, {'name':'Milhouse', 'age':10}, {'name':'Bart', 'age':10} ]
        sortedlist = sorted(my_list , key=lambda elem: "%02d %s" % (elem['age'], elem['name']))
        

        这是相当骇人听闻的,因为它依赖于将值转换为单个字符串表示形式进行比较,但它对包括负数在内的数字按预期工作(尽管如果您正在使用,您将需要使用零填充适当地格式化您的字符串数字)。

        【讨论】:

        • 使用稳定的timsort进行排序,可以多次调用sorted对多个条件进行排序
        • njzk2 的评论对我来说不是很清楚,所以我发现了以下内容。您可以按照 njzk2 的建议进行两次排序,或者将多个参数传递给最佳答案中的 operator.itemgetter。链接:stackoverflow.com/questions/5212870/…
        • 无需转成字符串。只需返回一个元组作为键。
        • 多次排序是最简单的通用解决方案,无需 hack:stackoverflow.com/a/29849371/1805397
        【解决方案9】:

        使用来自 Perl 的 Schwartzian transform

        py = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
        

        sort_on = "name"
        decorated = [(dict_[sort_on], dict_) for dict_ in py]
        decorated.sort()
        result = [dict_ for (key, dict_) in decorated]
        

        给予

        >>> result
        [{'age': 10, 'name': 'Bart'}, {'age': 39, 'name': 'Homer'}]
        

        有关 Perl Schwartzian 变换的更多信息:

        在计算机科学中,Schwartzian 变换是一种 Perl 编程 用于提高排序项目列表效率的成语。这 当排序为时,习语适用于基于比较的排序 实际上是基于某个属性(键)的排序 元素,其中计算该属性是一项密集操作, 应该执行最少的次数。施瓦茨人 Transform 值得注意的是它不使用命名的临时数组。

        【讨论】:

        • Python 从 2.4 开始支持 key=.sort,也就是 2004 年,它在 C 中的排序代码中进行 Schwartzian 变换;因此该方法仅在 Python 2.0-2.3 上有用。所有这些都超过 12 岁。
        【解决方案10】:

        假设我有一本字典D,其中包含以下元素。要进行排序,只需使用 sorted 中的 key 参数传递自定义函数,如下所示:

        D = {'eggs': 3, 'ham': 1, 'spam': 2}
        def get_count(tuple):
            return tuple[1]
        
        sorted(D.items(), key = get_count, reverse=True)
        # Or
        sorted(D.items(), key = lambda x: x[1], reverse=True)  # Avoiding get_count function call
        

        签出this

        【讨论】:

          【解决方案11】:

          这是另一种通用解决方案 - 它通过键和值对 dict 的元素进行排序。

          它的优点 - 不需要指定键,如果某些字典中缺少某些键,它仍然可以工作。

          def sort_key_func(item):
              """ Helper function used to sort list of dicts
          
              :param item: dict
              :return: sorted list of tuples (k, v)
              """
              pairs = []
              for k, v in item.items():
                  pairs.append((k, v))
              return sorted(pairs)
          sorted(A, key=sort_key_func)
          

          【讨论】:

          • “按键和值对 dict 的元素进行排序”是什么意思?它以什么方式排序?价值观从何而来?
          【解决方案12】:

          使用Pandas 包是另一种方法,尽管它在大规模运行时比其他人提出的更传统的方法慢得多:

          import pandas as pd
          
          listOfDicts = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
          df = pd.DataFrame(listOfDicts)
          df = df.sort_values('name')
          sorted_listOfDicts = df.T.to_dict().values()
          

          以下是小列表和大 (100k+) dicts 列表的一些基准值:

          setup_large = "listOfDicts = [];\
          [listOfDicts.extend(({'name':'Homer', 'age':39}, {'name':'Bart', 'age':10})) for _ in range(50000)];\
          from operator import itemgetter;import pandas as pd;\
          df = pd.DataFrame(listOfDicts);"
          
          setup_small = "listOfDicts = [];\
          listOfDicts.extend(({'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}));\
          from operator import itemgetter;import pandas as pd;\
          df = pd.DataFrame(listOfDicts);"
          
          method1 = "newlist = sorted(listOfDicts, key=lambda k: k['name'])"
          method2 = "newlist = sorted(listOfDicts, key=itemgetter('name')) "
          method3 = "df = df.sort_values('name');\
          sorted_listOfDicts = df.T.to_dict().values()"
          
          import timeit
          t = timeit.Timer(method1, setup_small)
          print('Small Method LC: ' + str(t.timeit(100)))
          t = timeit.Timer(method2, setup_small)
          print('Small Method LC2: ' + str(t.timeit(100)))
          t = timeit.Timer(method3, setup_small)
          print('Small Method Pandas: ' + str(t.timeit(100)))
          
          t = timeit.Timer(method1, setup_large)
          print('Large Method LC: ' + str(t.timeit(100)))
          t = timeit.Timer(method2, setup_large)
          print('Large Method LC2: ' + str(t.timeit(100)))
          t = timeit.Timer(method3, setup_large)
          print('Large Method Pandas: ' + str(t.timeit(1)))
          
          #Small Method LC: 0.000163078308105
          #Small Method LC2: 0.000134944915771
          #Small Method Pandas: 0.0712950229645
          #Large Method LC: 0.0321750640869
          #Large Method LC2: 0.0206089019775
          #Large Method Pandas: 5.81405615807
          

          【讨论】:

          • 我运行了你的代码,发现大型方法 Pandas 的 timeit.Timer 参数中有一个错误:你指定了“setup_small”,它应该是“setup_large”。更改该 arg 导致程序运行未完成,我在超过 5 分钟后停止了它。当我用“timeit(1)”运行它时,Large Method Pandas 在 7.3 秒内完成,比 LC 或 LC2 差很多。
          • 你说的很对,这是我的疏忽。我不再推荐它用于大箱子!我已经编辑了答案以简单地允许它作为一种可能性,用例仍有待辩论。
          【解决方案13】:
          a = [{'name':'Homer', 'age':39}, ...]
          
          # This changes the list a
          a.sort(key=lambda k : k['name'])
          
          # This returns a new list (a is not modified)
          sorted(a, key=lambda k : k['name']) 
          

          【讨论】:

            【解决方案14】:

            有时我们需要使用lower()。例如,

            lists = [{'name':'Homer', 'age':39},
              {'name':'Bart', 'age':10},
              {'name':'abby', 'age':9}]
            
            lists = sorted(lists, key=lambda k: k['name'])
            print(lists)
            # [{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}, {'name':'abby', 'age':9}]
            
            lists = sorted(lists, key=lambda k: k['name'].lower())
            print(lists)
            # [ {'name':'abby', 'age':9}, {'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}]
            

            【讨论】:

            • 为什么在这种情况下我们需要使用 lower()?
            【解决方案15】:

            如果您不需要dictionaries 的原始list,您可以使用自定义键函数使用sort() 方法就地修改它。

            按键功能:

            def get_name(d):
                """ Return the value of a key in a dictionary. """
            
                return d["name"]
            

            要排序的list

            data_one = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]
            

            就地排序:

            data_one.sort(key=get_name)
            

            如果您需要原始的list,请调用sorted() 函数,将list 和key 函数传递给它,然后将返回的排序后的list 分配给一个新变量:

            data_two = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]
            new_data = sorted(data_two, key=get_name)
            

            打印data_onenew_data

            >>> print(data_one)
            [{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
            >>> print(new_data)
            [{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
            

            【讨论】:

              【解决方案16】:

              我一直是 lambda 过滤器的忠实粉丝。但是,如果考虑时间复杂度,这不是最佳选择。

              第一个选项

              sorted_list = sorted(list_to_sort, key= lambda x: x['name'])
              # Returns list of values
              

              第二个选项

              list_to_sort.sort(key=operator.itemgetter('name'))
              # Edits the list, and does not return a new list
              

              执行时间的快速比较

              # First option
              python3.6 -m timeit -s "list_to_sort = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}, {'name':'Faaa', 'age':57}, {'name':'Errr', 'age':20}]" -s "sorted_l=[]" "sorted_l = sorted(list_to_sort, key=lambda e: e['name'])"
              

              1000000 次循环,3 次中的最佳:每个循环 0.736 微秒

              # Second option
              python3.6 -m timeit -s "list_to_sort = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}, {'name':'Faaa', 'age':57}, {'name':'Errr', 'age':20}]" -s "sorted_l=[]" -s "import operator" "list_to_sort.sort(key=operator.itemgetter('name'))"
              

              1000000 次循环,3 次中的最佳:每个循环 0.438 微秒

              【讨论】:

                【解决方案17】:

                如果性能是一个问题,我会使用operator.itemgetter 而不是lambda,因为内置函数比手工制作的函数执行得更快。根据我的测试,itemgetter 函数的执行速度似乎比 lambda 快大约 20%。

                来自https://wiki.python.org/moin/PythonSpeed

                同样,内置函数的运行速度比手动构建的等效函数快。例如 map(operator.add, v1, v2) 比 map(lambda x,y: x+y, v1, v2) 快。

                这是使用lambdaitemgetter 的排序速度比较。

                import random
                import operator
                
                # Create a list of 100 dicts with random 8-letter names and random ages from 0 to 100.
                l = [{'name': ''.join(random.choices(string.ascii_lowercase, k=8)), 'age': random.randint(0, 100)} for i in range(100)]
                
                # Test the performance with a lambda function sorting on name
                %timeit sorted(l, key=lambda x: x['name'])
                13 µs ± 388 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
                
                # Test the performance with itemgetter sorting on name
                %timeit sorted(l, key=operator.itemgetter('name'))
                10.7 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
                
                # Check that each technique produces the same sort order
                sorted(l, key=lambda x: x['name']) == sorted(l, key=operator.itemgetter('name'))
                True
                

                两种技术都以相同的顺序对列表进行排序(通过执行代码块中的最后一条语句来验证),但第一种技术要快一些。

                【讨论】:

                  【解决方案18】:

                  正如@Claudiu 在this answer 的评论部分中向@monojohnny 指出的那样,
                  给出:

                  list_to_be_sorted = [
                                        {'name':'Homer', 'age':39}, 
                                        {'name':'Milhouse', 'age':10}, 
                                        {'name':'Bart', 'age':10} 
                                      ]
                  

                  按键排序字典列表'age','name'
                  (如SQL语句ORDER BY age, name),你可以使用:

                  newlist = sorted( list_to_be_sorted, key=lambda k: (k['age'], k['name']) )
                  

                  或者,同样

                  import operator
                  newlist = sorted( list_to_be_sorted, key=operator.itemgetter('age','name') )
                  

                  print(newlist)

                  [{'name': 'Bart', 'age': 10},
                  {'name': 'Milhouse', 'age': 10},
                  {'name': 'Homer', 'age': 39}]

                  【讨论】:

                    猜你喜欢
                    • 2011-02-22
                    • 2010-11-15
                    • 1970-01-01
                    相关资源
                    最近更新 更多