Выборочное слияние по словарям Python - PullRequest
0 голосов
/ 06 октября 2018

У меня есть 2 словаря в Python (d1, d2), где мне нужно передать отсутствующий элемент «id» из d2 в d1, игнорируя любые другие различия (например, дополнительный «потомок» в d1).Что действительно необходимо, так это то, что словарь результатов - просто d1 с добавленными элементами «id».Я попытался объединить, но это не сработало, так как в любом случае я теряю данные.

d1 = {
    "parent": {
        "name": "Axl",
        "surname": "Doe",
        "children": [
            {
                "name": "John",
                "surname": "Doe"
            },                
            {
                "name": "Jane",
                "surname": "Doe",
                "children": [
                    {
                        "name": "Jim",
                        "surname": "Doe"
                    },
                    {
                        "name": "Kim",
                        "surname": "Doe"
                    }
                ]
            }
        ]
    }
}

d2 = {
    "parent": {
        "id": 1,
        "name": "Axl",
        "surname": "Doe",
        "children": [
            {
                "id": 2,
                "name": "John",
                "surname": "Doe"
            },
            {
                "id": 3,
                "name": "Jane",
                "surname": "Doe",
                "children": [
                    {
                        "id": 4,
                        "name": "Jim",
                        "surname": "Doe"
                    },
                    {
                        "id": 5,
                        "name": "Kim",
                        "surname": "Doe"
                    },
                    {
                        "id": 6
                        "name": "Bill",
                        "surname": "Doe"
                    },
                ]
            }
        ]
    }
}

result = {
"parent": {
    "id": 1,
    "name": "Axl",
    "surname": "Doe",
    "children": [
        {
            "id": 2,
            "name": "John",
            "surname": "Doe"
        },
        {
            "id": 3,
            "name": "Jane",
            "surname": "Doe",
            "children": [
                {
                    "id": 4,
                    "name": "Jim",
                    "surname": "Doe"
                },
                {
                    "id": 5,
                    "name": "Kim",
                    "surname": "Doe"
                }
            ]
        }
    ]
}

}

Есть идеи?

Ответы [ 2 ]

0 голосов
/ 06 октября 2018

Я хотел бы добавить итеративный ответ вместо рекурсивного ответа, поскольку он, вероятно, окажется более эффективным.

Он не достигнет какого-либо порога стека и будет немного быстрее:

import operator

root = main_dict["parent"]
lookup_root = id_lookup_dict["parent"]

keyfunc = operator.itemgetter("name", "surname")

def _recursive_fill_id(root, lookup_root, keyfunc):
    """Recursively fill root node with IDs

    Matches nodes according to keyfunc
    """
    matching_nodes = [(root, lookup_root)]

    while matching_nodes:
        root, lookup_root = matching_nodes.pop()
        root["id"] = lookup_root["id"]

        # Fetch children
        root_children = root.get("children")

        # There are no children
        if root_children is None:
            continue

        children_left = len(root_children)

        # Create a dict mapping the key identifying a child to the child
        # This avoids a hefty lookup cost and requires a single iteration.
        children_dict = dict(zip(map(keyfunc, root_children), root_children))

        for lookup_child in lookup_root["children"]:
            lookup_key = keyfunc(lookup_child)
            matching_child = children_dict.get(lookup_key)

            if matching_child is not None:
                matching_nodes.append((matching_child, lookup_child))

                # Short circuit in case all children were filled
                children_left -= 1
                if not children_left:
                    break


_recursive_fill_id(root, lookup_root, keyfunc)
0 голосов
/ 06 октября 2018

Я сопоставляю детей в соответствии с ключевой функцией, в данном случае атрибутами «name» и «фамилия».

Затем я перехожу к диктовке id_lookup (названной d2 в вашем примере) и пробуюсопоставить каждого ребенка с детьми main_dict.Если я найду совпадение, я вернусь к нему.

В конце main_dict (или d1 в вашем примере) заполняется идентификаторами: -)

import operator

root = main_dict["parent"]
lookup_root = id_lookup_dict["parent"]

keyfunc = operator.itemgetter("name", "surname")

def _recursive_fill_id(root, lookup_root, keyfunc):
    """Recursively fill root node with IDs

    Matches nodes according to keyfunc
    """
    root["id"] = lookup_root["id"]

    # Fetch children
    root_children = root.get("children")

    # There are no children
    if root_children is None:
        return

    children_left = len(root_children)

    # Create a dict mapping the key identifying a child to the child
    # This avoids a hefty lookup cost and requires a single iteration.
    children_dict = dict(zip(map(keyfunc, root_children), root_children))

    for lookup_child in lookup_root["children"]:
        lookup_key = keyfunc(lookup_child)
        matching_child = children_dict.get(lookup_key)

        if matching_child is not None:
            _recursive_fill_id(matching_child, lookup_child, keyfunc)

            # Short circuit in case all children were filled
            children_left -= 1
            if not children_left:
                break

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