Search code examples
python-3.xdictionaryarraylistkeykey-value

New dict filtered by item


Could you help me with this simple stuff, unfortunately I don't get it.

I have this a list with two another lists with lists of dicts (but it can be more lists).

a = [
    [
        {'DESCRIP': '', 'INTF': 'Vl77', 'PROTOCOL': 'up', 'STATUS': 'up'},
        {'DESCRIP': '', 'INTF': 'Fa0', 'PROTOCOL': 'down', 'STATUS': 'admin down'}
    ],
    [
        {'INBOUND_ACL': '', 'INTF': 'Vlan77', 'IPADDR': ['192.168.77.11/24'], 'IP_HELPER': [], 'LINK_STATUS': 'up', 'MTU': '1500', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'up', 'VRF': ''},
        {'INBOUND_ACL': '', 'INTF': 'FastEthernet0', 'IPADDR': [], 'IP_HELPER': [], 'LINK_STATUS': 'administratively down', 'MTU': '', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'down', 'VRF': ''}
    ]
]

My goal is to get a final list of merged dicts, derived by a rule ('merge all dicts that contain the 'INTF' field with the same number, in this case 77 or 0 , in other words to filter by interface number').

Like that

new_dict = [
            {'DESCRIP': '', 'PROTOCOL': 'up', 'STATUS': 'up','INBOUND_ACL': '', 'INTF': 'Vlan77', 'IPADDR': ['192.168.77.11/24'], 'IP_HELPER': [], 'LINK_STATUS': 'up','MTU': '1500', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'up', 'VRF': ''},
            {'DESCRIP': '', 'PROTOCOL': 'down', 'STATUS': 'admin down','INBOUND_ACL': '', 'INTF': 'FastEthernet0', 'IPADDR': [], 'IP_HELPER': [], 'LINK_STATUS': 'administratively down', 'MTU': '', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'down', 'VRF': ''}
       ]

Solution

  • Since we are looking to merge dicts based on a key, I think the best place to start will be to reshape our data to facilitate a key based lookup. While we are reshaping the data, let's also create a key that is just the number.

    import re
    a_reshaped = [
        {re.search(r'\d+', x["INTF"]): x for x in y}
        for y in a
    ]
    print(a_reshaped )
    

    This will give us:

    [
        {
            '77': {'DESCRIP': '', 'INTF': 'Vl77', 'PROTOCOL': 'up', 'STATUS': 'up'},
            '0': {'DESCRIP': '', 'INTF': 'Fa0', 'PROTOCOL': 'down', 'STATUS': 'admin down'}
        },
        {
            '77': {'INBOUND_ACL': '', 'INTF': 'Vlan77', 'IPADDR': ['192.168.77.11/24'], 'IP_HELPER': [], 'LINK_STATUS': 'up', 'MTU': '1500', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'up', 'VRF': ''},
            '0': {'INBOUND_ACL': '', 'INTF': 'FastEthernet0', 'IPADDR': [], 'IP_HELPER': [], 'LINK_STATUS': 'administratively down', 'MTU': '', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'down', 'VRF': ''}
        }
    ]
    

    At this point we can iterate over a_reshaped and merge the dicts based on the key. We can base this part on either setdefault() or collections.defaultdict(). I prefer the second myself.

    import collections
    results = collections.defaultdict(dict)
    for c in a_reshaped:
        for key, value in c.items():
            results[key] = {**results[key], **value}
    print(list(results.values()))
    

    Giving us:

    [
        {'DESCRIP': '', 'INTF': 'Vlan77', 'PROTOCOL': 'up', 'STATUS': 'up', 'INBOUND_ACL': '', 'IPADDR': ['192.168.77.11/24'], 'IP_HELPER': [], 'LINK_STATUS': 'up', 'MTU': '1500', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'up', 'VRF': ''},
        {'DESCRIP': '', 'INTF': 'FastEthernet0', 'PROTOCOL': 'down', 'STATUS': 'admin down', 'INBOUND_ACL': '', 'IPADDR': [], 'IP_HELPER': [], 'LINK_STATUS': 'administratively down', 'MTU': '', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'down', 'VRF': ''}
    ]
    

    The complete solution based on collections.defaultdict() being:

    import re
    import collections
    
    a = [
        [
            {'DESCRIP': '', 'INTF': 'Vl77', 'PROTOCOL': 'up', 'STATUS': 'up'},
            {'DESCRIP': '', 'INTF': 'Fa0', 'PROTOCOL': 'down', 'STATUS': 'admin down'}
        ],
        [
            {'INBOUND_ACL': '', 'INTF': 'Vlan77', 'IPADDR': ['192.168.77.11/24'], 'IP_HELPER': [], 'LINK_STATUS': 'up', 'MTU': '1500', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'up', 'VRF': ''},
            {'INBOUND_ACL': '', 'INTF': 'FastEthernet0', 'IPADDR': [], 'IP_HELPER': [], 'LINK_STATUS': 'administratively down', 'MTU': '', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'down', 'VRF': ''}
        ]
    ]
    
    a_reshaped = [
        {re.search(r'\d+', x["INTF"]).group(): x for x in y}
        for y in a
    ]
    
    results = collections.defaultdict(dict)
    for c in a_reshaped:
        for key, value in c.items():
            results[key] = {**results[key], **value}
    print(list(results.values()))
    

    or based on setdefault() as:

    import re
    
    a = [
        [
            {'DESCRIP': '', 'INTF': 'Vl77', 'PROTOCOL': 'up', 'STATUS': 'up'},
            {'DESCRIP': '', 'INTF': 'Fa0', 'PROTOCOL': 'down', 'STATUS': 'admin down'}
        ],
        [
            {'INBOUND_ACL': '', 'INTF': 'Vlan77', 'IPADDR': ['192.168.77.11/24'], 'IP_HELPER': [], 'LINK_STATUS': 'up', 'MTU': '1500', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'up', 'VRF': ''},
            {'INBOUND_ACL': '', 'INTF': 'FastEthernet0', 'IPADDR': [], 'IP_HELPER': [], 'LINK_STATUS': 'administratively down', 'MTU': '', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'down', 'VRF': ''}
        ]
    ]
    
    a_reshaped = [
        {re.search(r'\d+', x["INTF"]).group(): x for x in y}
        for y in a
    ]
    
    results = {}
    for c in a_reshaped:
        for key, value in c.items():
            results[key] = {**results.setdefault(key, {}), **value}
    
    print(list(results.values()))
    

    Both will give you:

    [
        {'DESCRIP': '', 'INTF': 'Vlan77', 'PROTOCOL': 'up', 'STATUS': 'up', 'INBOUND_ACL': '', 'IPADDR': ['192.168.77.11/24'], 'IP_HELPER': [], 'LINK_STATUS': 'up', 'MTU': '1500', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'up', 'VRF': ''},
        {'DESCRIP': '', 'INTF': 'FastEthernet0', 'PROTOCOL': 'down', 'STATUS': 'admin down', 'INBOUND_ACL': '', 'IPADDR': [], 'IP_HELPER': [], 'LINK_STATUS': 'administratively down', 'MTU': '', 'OUTGOING_ACL': '', 'PROTOCOL_STATUS': 'down', 'VRF': ''}
    ]