在Python 3中按键对具有不同类型的字典排序列表进行排序

时间:2018-07-01 17:23:22

标签: python python-3.x sorting

我有一种通过键将字典列表分组的方法。为此,我发现here必须使用groupby函数,但必须对列表进行排序。这是我现在的方法:

def group_list_by_key(data, key):
    data.sort(key=lambda x: x[key])
    result = []
    for k, v in groupby(data, key=lambda x: x[key]):
        result.append(list(v))
    return result

仅当在所有字典中定义了每个键并且值都属于同一类型时,这段代码才有效。但是,在使用这种方法的地方,我不知道该密钥是否在各处定义,并且它们是否属于同一类型。在Python 2.x上,我知道存在带有cmp参数的sorted函数,该函数可以进行自定义排序,但是从Python 3.x开始,这已经不可能了。有没有办法进行自定义排序?我正在考虑使用通过<进行的经典排序以及通过类型名进行的排序。

现在,我考虑使用get函数并将其转换为类似这样的字符串

data.sort(key=lambda x: str(x.get(key)))
...
for k, v in groupby(data, key=lambda x: x.get(key)):

仅克服了字符串,数字和无内容的情况,但克服了通用对象,例如在我执行时很容易破坏

a = [{'b': 0, 'c': 1}, {'b': '0'}, {'b': 0, 'c': 2}, {'b': 1}, {'c': 3}]
group_list_by_key(a, 'b')

输出为

[[{'b': 0, 'c': 1}], [{'b': '0'}], [{'b': 0, 'c': 2}], [{'b': 1}], [{'c': 3}]]

而不是我的预期(列表顺序不是问题)

[[{'b': 0, 'c': 1}, {'b': 0, 'c': 2}], [{'b': '0'}], [{'b': 1}], [{'c': 3}]]

2 个答案:

答案 0 :(得分:2)

您可以通过执行以下操作来解决问题

data = [{'b': 0, 'c': 1}, {'b': '0'}, {'b': 0, 'c': 2}, {'b': 1}, {'c': 3}]
key='b'

def f(x):
     ret = x.get(key, -1)
     return ret if type(ret) == int else -2

result = [list(v) for k, v in groupby(sorted(data, key=f), f)]

# result: [[{'b': '0'}], [{'c': 3}], [{'b': 0, 'c': 1}, {'b': 0, 'c': 2}], [{'b': 1}]]

但是,如果您仍然需要自定义比较功能,则可以使用functools.cmp_to_key

import functools
sorted(x, key=functools.cmp_to_key(custom_cmp_function))

答案 1 :(得分:0)

感谢@Sunitha和@ njzk2指出了cmp_to_key函数,它完全可以实现我想要的功能。所以,我现在的分组是:

from functools import cmp_to_key
from itertools import groupby

def group_list_by_key(data, key):
    def compare_values_types(a, b):
        a = a.get(key)
        b = b.get(key)
        if a.__class__ == b.__class__:
            if a < b:
                return -1
            elif a > b:
                return 1
            else:
                return 0
        else:
            if a.__class__.__name__ < b.__class__.__name__:
                return -1
            elif a.__class__.__name__ > b.__class__.__name__:
                return 1
            else:
                return 0
    data.sort(key=cmp_to_key(compare_values_types))
    return [list(v) for k, v in groupby(data, key=lambda x: x.get(key))]

调用样本列表

a = [{'b': 0, 'c': 1}, {'b': '0'}, {'b': 0, 'c': 2}, {'b': 1}, {'c': 3}]
group_list_by_key(a, 'b')

它返回预期的列表

[[{'c': 3}], [{'b': 0, 'c': 1}, {'b': 0, 'c': 2}], [{'b': 1}], [{'b': '0'}]]

我所做的是以经典方式比较相同类型的键,否则,我只是在类名称之间进行字符串比较(使用a.__class__.__name__而不是type(a).__name__,请检查answer)。 谢谢大家!