Python - удаление элементов из вложенного словаря - PullRequest
0 голосов
/ 12 сентября 2018

Я использую фрагмент кода ниже, чтобы сравнить два объекта, игнорируя при этом несколько ключей.Тем не менее, код не работает, когда я пытаюсь удалить поле из вложенного словаря.Мне нужно игнорировать A, а также lastElement из C.Приведенный ниже код может игнорировать A, но не lastElement из C.

Код :

def equal_dicts(d1, d2, ignore_keys=()):
    d1_, d2_ = d1.copy(), d2.copy()
    for k in ignore_keys:
        try:
            del d1_[k]
        except KeyError:
            pass
        try:
            del d2_[k]
        except KeyError:
            pass
    return json.dumps(d1_, sort_keys=True) == json.dumps(d2_, sort_keys=True)

Попытка выполнить вышеуказанное как :

equal_dicts(data1, data2, ('A', 'C'['lastElement']))

Ожидаемый результат: True.

data1 :

{
    "A": "123456789",
    "B": {
        "firstElement": "abc",
        "lastElement": "def"
    },
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    }
}

data2 :

{
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    },
    "A": "78901234",
    "B": {
        "firstElement": "abc",
        "lastElement": "e"
    }
}

Ответы [ 3 ]

0 голосов
/ 12 сентября 2018

Большая часть проблемы в том, что у вас нет правильной спецификации для вложенных ключей.Очевидно, вы не можете индексировать строку с другой строкой, поэтому 'C'['lastElement'] не даст вам ничего, кроме ошибки.Давайте использовать кортеж или другую итерацию для хранения вложенных ключей:

equal_dicts(data1, data2, ('A', ('C', 'lastElement')))

Теперь довольно легко очистить словари:

def remove_key(d, k):
    if not isinstance(d, dict): return
    try:
        if isinstance(k, str) or len(k) == 1:
            if not isinstance(k, str): k = k[0]
            del d[k]
        else:
            remove_key(d[k[0]], k[1:])
    except KeyError:
        pass

Просто используйте эту функцию вместо del.

Имейте в виду, что сделанная вами копия мелкая: удаление вложенных ключей фактически удалит их и из исходных объектов.Вы можете противодействовать этому, обновив функцию remove_key, чтобы она возвращала обновленный словарь по мере необходимости только при удалении ключа.Это не будет намного дешевле, чем, скорее всего, сделать глубокую копию, но ее будет немного легче читать:

def remove_key(d, key):
    if not isinstance(d, dict):
        return d
    if not isinstance(key, str):
        key, *rem = key
    else:
        rem = []
    if key not in d:
        return d
    if not rem:
        return {k: v for k, v in d.items() if k != key}
    e = remove_key(d[key], rem)
    if e is not d[key]:
         return {k: e if k == key else v for k, v in d.items()}
    return d

Используйте эту версию для назначения копий:

for key in ignore_keys:
    d1 = remove_key(d1, key)
    d2 = remove_key(d2, key)

Если ключ не будет удален, он останется исходной ссылкой.Любой удаленный ключ вызовет копирование только необходимых уровней вложенного словаря, хотя это может произойти несколько раз для данного уровня.

В качестве окончательного возвращаемого значения просто используйте return d1 == d2.Сравнение по словарю осуществляется по фактическому ключу и значению без учета сортировки.

0 голосов
/ 12 сентября 2018

Функция удаления ключей кажется неправильной. Кроме того, я постарался внести минимальные изменения в ваш код.

from functools import reduce
import json

def delete_nested(dictionary, paths):
    """
    Delete the keys specified as path in paths from dictionary.
    """
    for path in paths:
        parent_path, last_key = path[:-1], path[-1]
        parent = reduce(dict.get, parent_path, dictionary)
        if(parent==None):
            sys.exit("The path {path} is invalid".format(path=path))
        if(not(isinstance(parent, dict))):
            sys.exit("The path {path} doesn't contain a dict".format(path=parent_path))

        del parent[last_key]

def equal(d1, d2, ignore_keys=[]):
    """
    Check if d1 and d2 are equal less the ignore_keys
    """
    d1_, d2_ = d1.copy(), d2.copy()
    delete_nested(d1_, ignore_keys)
    delete_nested(d2_, ignore_keys)
    return d1_ == d2_

Пример выполнения:

d1 = json.loads("""{
    "A": "123456789",
    "B": {
        "firstElement": "abc",
        "lastElement": "def"
    },
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    }
}""")

d2 = json.loads("""{
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    },
    "A": "78901234",
    "B": {
        "firstElement": "abc",
        "lastElement": "e"
    }
}""")

print(equal(d1, d2, ["A",["B","lastElement"]])) # prints True
0 голосов
/ 12 сентября 2018

Вы можете использовать рекурсию для удаления ключей из вложенных словарей. Ниже приведен рабочий пример.

import json


def update_dict(original_dict, keys):
    return_dict = original_dict.copy()

    for k in keys:
        if isinstance(k, dict):
            _key = list(k.keys())[0]
            return_dict[_key] = update_dict(original_dict[_key], k.values())
        else:
            try:
                del return_dict[k]
            except KeyError:
                pass
    return return_dict


def equal_dicts(d1, d2, ignore_keys=()):
    d1_, d2_ = update_dict(d1, ignore_keys), update_dict(d2, ignore_keys)
    return json.dumps(d1_, sort_keys=True) == json.dumps(d2_, sort_keys=True)


data1 = {
    "A": "123456789",
    "B": {
        "firstElement": "abc",
        "lastElement": "def"
    },
    "C": {
        "firstElement": "chi",
        "lastElement": "abc"
    }
}

data2 = {
    "A": "78901234",
    "B": {
        "firstElement": "abc",
        "lastElement": "def"
    },
    "C": {
        "firstElement": "chi",
        "lastElement": "jul"
    }
}

print(equal_dicts(data1, data2, ('A', 'dateTime', 'trxId', {'C':'lastElement'})))

Я не уверен, соответствует ли это вашим требованиям, но теперь вы можете перейти в словарь. В этом словаре будут удалены только ключи из ожидаемого родителя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...