【问题标题】:Python Sort and Compare Nested DictionariesPython 对嵌套字典进行排序和比较
【发布时间】:2017-12-20 20:55:38
【问题描述】:

我正在尝试对包含字典列表和Python 3.6 中的其他数据的字典进行排序和比较。我不确定比较两者的最佳方法。两个字典中的数据是一样的,但是我无法控制它们给出的顺序。数据如下所示:

dict_A = {
    'addresses': [
        {'address': 'Tribal Land', 'address_country': 'AB', 'city': None, 'postal_code': None, 'state': 'GY'},
        {'address': 'Userland', 'address_country': 'ND', 'city': None, 'postal_code': None, 'state': 'KY'}],
    'name': 'FooBar', 
    'dob': None, 
    'ids':[
        {'date': None, 'country': None, 'number': 'Male', 'type': 'Gender', 'location': 'USA'},
        {'date': None, 'country': 'VE', 'number': '1234567', 'type': 'Foo No.', 'location': 'USA'}]
}


dict_B = {
    'addresses': [
        {'address': 'Userland', 'address_country': 'ND', 'city': None, 'postal_code': None, 'state': 'KY'},
        {'address': 'Tribal Land', 'address_country': 'AB', 'city': None, 'postal_code': None, 'state': 'GY'}],
    'dob': None, 
    'id':[
        {'country': 'VE', 'date': None, 'type': 'Foo No.', 'location': 'USA', 'number': '1234567'},
        {'country': None, 'date': None, 'type': 'Gender', 'location': 'USA', 'number': 'Male'}],
    'name': 'FooBar'
} 

我正在尝试与评估为 Truedict_A == dict_B 进行比较。

我尝试遍历字典,将项目发送给 Pandas 并将 dict 设置为 ordered_dict,但这似乎不起作用。我不确定最好的方法。

        # Loop over data, and conver the list of dicts to data frame for sorting,
            # then take the data, once sorted, and put it back into list of dicts
            for key, val in dict_A.items():
                if type(val) is list:
                    val.sort(key=lambda x: x if isinstance(x, str) else "")
                    dataframe = pd.DataFrame(val, index=range(len(val)))
                    dataframe.sort_values(by=dataframe.columns[0])
                    new_val = [OrderedDict(row) for i, row in dataframe.iterrows()]
                    dict_A.update({key: new_val})

也许更好的方法是将字典设置为列表,然后以这种方式进行比较?

【问题讨论】:

  • 键/值对没有多大意义:'number': 'Male''type': 'Gender''country': None'location': 'USA'
  • 可能是他当场制作的随机数据?我会建议 Mockaroo 处理这种事情。
  • @srig,这是我虚构的数据。最终只是为了举例
  • 当您知道它们之间的唯一区别是列表不遵循顺序时,为什么还要尝试对它们进行排序?
  • 我想比较两个数据集。如果不是很明显,上面的数据是人为的,但我正在比较的真实数据具有类似的嵌套数据结构。我正在将一组已知数据与来自 API 的一组未知数据进行比较。有时会有匹配,有时会有不同的数据。我需要知道那是什么时候。

标签: python python-3.x list sorting dictionary


【解决方案1】:

因此,如果您只有列表和字典,您可以在每个列表和字典上调用自定义的 equal 方法。例如,

def list_equal(l1, l2):
    if type(l1[0]) is dict:
        if len(l1) != len(l2):
            return False
        for i in range(len(l1)):
            if not l1.count(l1[i]) == l2.count(l1[i]):
                 return False
        return True
    return sorted(l1) == sorted(l2)

然后

def structures_equal(s1, s2):
    if not sorted(list(set(s1.keys()))) == sorted(list(set(s2.keys()))):
        return False
    for key in s1:
        if type(s1[key]) is list:
            if not type(s2[key]) is list:
                return False
            elif not list_equal(s1[key], s2[key]):
                return False
        elif not s1[key] == s2[key]:
            return False
        return True

现在列表比较在 O(n^2) 中运行,因为它计算每一行的实例。如果您从同一个数据源获取这些行,那么为每一行提取一个唯一 ID 也会很有用。然后时间变得明显更快,因为我们只比较每个列表中的 UIDS 和它们的计数。如果您可以将此作为 UIDS 的字典和分配的行的实例数,那就更好了。例如

[{'address': 'address0', 'foo': 'bar0', 'uid': 0},
 {'address': 'address1', 'foo': 'bar1', 'uid': 1},
 {'address': 'address2', 'foo': 'bar2', 'uid': 2},
 {'address': 'address3', 'foo': 'bar3', 'uid': 3},
 {'address': 'address4', 'foo': 'bar4', 'uid': 4},
 {'address': 'address0', 'foo': 'bar0', 'uid': 0},
 {'address': 'address1', 'foo': 'bar1', 'uid': 1},
 {'address': 'address2', 'foo': 'bar2', 'uid': 2}]

变成

{0: [{'address': 'address0', 'foo': 'bar0', 'uid': 0},
     {'address': 'address0', 'foo': 'bar0', 'uid': 0}],
 1: [{'address': 'address1', 'foo': 'bar1', 'uid': 1},
     {'address': 'address1', 'foo': 'bar1', 'uid': 1}],
 2: [{'address': 'address2', 'foo': 'bar2', 'uid': 2},
     {'address': 'address2', 'foo': 'bar2', 'uid': 2}],
 3: [{'address': 'address3', 'foo': 'bar3', 'uid': 3}],
 4: [{'address': 'address4', 'foo': 'bar4', 'uid': 4}]}

这个算法将是

def list_converted_to_dict_equal(d1, d2):
    for key in d1:
        if key not in d2 or len(d1[key]) != len(d2[key]):
            return False
    return True

这比以前好多了。

【讨论】:

  • 我没有顶级词典中的所有列表和词典。我也有一些独立的字符串值,比如'name':'foobar'。其中一些键/值对具有None 值,例如'name': None
  • 这就是我添加最后一个elif 语句的原因。
  • 另外,您是否有addressesid 中的dicts 的主键或列表中包含dicts 的任何其他内容?
  • 没有主键。只是数据本身
  • 那么这个解决方案应该可以工作。另外,请将 dict_b 中的 id 键替换为 ids 以匹配 dict_a
【解决方案2】:

将您的字典转换为数据结构,即真正的类。

对于此类,如果您希望能够对它们进行排序,请为每个对象重载 __cmp__ 方法。

重载__eq__,如果你想知道两个对象是否相等。

class ApiDto(object):
    def __cmp__ (self, other):
        pass
    def __eq__ (self, other):
        pass

class Address(object):
    def __cmp__ (self, other):
        pass
    def __eq__ (self, other):
        pass

class Id(object):
    def __cmp__ (self, other):
        pass
    def __eq__ (self, other):
        pass

将字典更改为现在使用上述类。

现在您可以随意排序和比较,而无需一次处理所有属性。


如果此时不明显,您现在拥有的 dict 将是一个 ApiDto,它有一个 name 字段,一个 addresses 字段,它是一个 Address 的列表,一个 ids 字段这是Id 的列表,最后是dob 字段。

当您为ApiDto 重载__cmp____eq__ 方法时,您将对所有类执行相同的操作,这将允许您对对象进行排序并最终将它们相互比较。

此外,如果您需要将对象转换回字典,您可以调用 __dict__ 属性来为您提供此功能

【讨论】:

  • 我不太确定我是否明白你在这里的建议。
  • @unseen_damage 我试图进一步扩展答案。
  • 对于这个特定的例子来说听起来有点矫枉过正,不是吗?
  • @MadPhysicist,这取决于 OP 希望使用这些对象的程度。留给我,我不会处理从这样的 API 检索到的原始字典,尤其是当它们如此复杂时。我更愿意走模块化与封装相结合的路线,这样我就不会争先恐后地分别处理每个单独的组件。
  • @smac89。我只是说这不是 OP 提出的问题的一个非常普遍的解决方案。我完全同意你的观点,数据最好放在模型中的特定对象中。然后您就可以完全控制,而不需要这些通用的慢速排序算法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-31
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多