Home > database >  Combine lists of dictionaries with different values
Combine lists of dictionaries with different values

Time:09-03

I have list of dictionaries with matching keys like this.

[{'account_general_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}, 
{'control_section_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}, 
{'account_general_permission': {'view': True, 'create': True, 'edit': True, 'delete': False}}, 
{'control_section_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}]

I need to combine all matching keys together with the value inside the dicts where if there is true for some key its given priority over false.

So result should look something like

{'account_general_permission': {'view': True, 'create': True, 'edit': True, 'delete': False}}, 
{'control_section_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}]

What would the fastest way to achieve this as speed is crucial to perform this.

My attempt Assuming list is sort based on keys and I have a variable x to know how many times a key repeats in a list


new_list = np.array_split(new_list, len(permission))

for i in new_list:
    for j in i:
        for k, l in j.items():
            for m, n in l.items():
                if n == True:
                    l[m] = n

This partially works, also doesn't look the cleanest.

CodePudding user response:

data = [{'account_general_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}, 
        {'control_section_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}, 
        {'account_general_permission': {'view': True, 'create': True, 'edit': True, 'delete': False}}, 
        {'control_section_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}]

# Let's make a new dictionary~
new_data = {}
for x in data:
    # Each "dictionary" in your list appears to just be a single key/value pair.
    key, values = tuple(x.items())[0]
    # If we've added this key to the dictionary already...
    if key in new_data:
        for k in values:
            # If that inner key isn't already True, or doesn't exist.
            if not new_data[key].get(k):
                # Overwrite this inner key's value OR add the new key:value pair.
                new_data[key][k] |= values[k]
    # Otherwise we can just add this dict to the new dict.
    else:
        new_data |= x

print(new_data)

# Output:

{'account_general_permission': {'view': True, 'create': True, 'edit': True, 'delete': False}, 
 'control_section_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}

An overkill, but one-line approached using pandas could look like:

data = (pd.concat([pd.DataFrame.from_dict(x, 'index') for x in data])
          .groupby(level=0)
          .max()
          .to_dict('index'))

print(data)

# Output

{'account_general_permission': {'view': True, 'create': True, 'edit': True, 'delete': False}, 
 'control_section_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}

CodePudding user response:

this should work

from collections import Counter

data = [{'account_general_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}, 
{'control_section_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}, 
{'account_general_permission': {'view': True, 'create': True, 'edit': True, 'delete': False}}, 
{'control_section_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}]

counters = {'account_general_permission': Counter(), 'control_section_permission': Counter()} 

for element in data:
    for key, data in element.items():
        counters[key].update(data)


result = [{key: {item: bool(val) for item, val in data.items()}} for key, data in counters.items()]
result

output:

{'account_general_permission': {'view': True, 'create': True, 'edit': True, 'delete': False}}, 
{'control_section_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}

CodePudding user response:

lod = [{'account_general_permission': {'view': False, 'create': False, 'edit':
                                       False, 'delete': False}},
       {'control_section_permission': {'view': False, 'create': False, 'edit':
                                       False, 'delete': False}},
       {'account_general_permission': {'view': True, 'create': True, 'edit':
                                       True, 'delete': False}},
       {'control_section_permission': {'view': False, 'create': False, 'edit':
                                       False, 'delete': False}}]

result = {}
for d in lod:
    perm_name, perms = next(iter(d.items()))
    result[perm_name] = {perm_elem: any([perm_val,
                                         result.get(perm_name, {}
                                                    ).get(perm_elem, False)])
                         for perm_elem, perm_val in perms.items()}
{'account_general_permission': {'create': True,
                                'delete': False,
                                'edit': True,
                                'view': True},
 'control_section_permission': {'create': False,
                                'delete': False,
                                'edit': False,
                                'view': False}}

The result is a dict of dicts, though, but I feel like it's a more natural data structure for this task.

CodePudding user response:

I don't know about the numpy stuff, but your example applied to the original list is already reasonable, especially considering that this is a small data set. Iteration is faster than indexing and a dictionary's update function that is written in C is a bit faster still. So, using a defaultdict to create items as needed, you could do

import collections

data = [{'account_general_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}, 
{'control_section_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}},
{'account_general_permission': {'view': True, 'create': True, 'edit': True, 'delete': False}},
{'control_section_permission': {'view': False, 'create': False, 'edit': False, 'delete': False}}]

resolved = collections.defaultdict(lambda: {'view': False, 'create': False,
    'edit': False, 'delete': False})

for perms in data:
    for perm_name, perm_vals in perms.items():
        resolved[perm_name].update((n,v) for n,v in perm_vals.items() if v)

for k,v in resolved.items():
    print(f"{k}: {v}")

This solution does not update the dictionaries in the original table.

CodePudding user response:

Let's get crazy and functional:

lod = [{'account_general_permission': {'view': False, 'create': False, 'edit':
                                       False, 'delete': False}},
       {'control_section_permission': {'view': False, 'create': False, 'edit':
                                       False, 'delete': False}},
       {'account_general_permission': {'view': True, 'create': True, 'edit':
                                       True, 'delete': False}},
       {'control_section_permission': {'view': False, 'create': False, 'edit':
                                       False, 'delete': False}}]

from functools import reduce
from itertools import groupby


def get_key(d):
    return next(iter(d.keys()))


def get_val(d):
    return next(iter(d.values()))


def merge_dicts(d, perm_d):
    unwrappe_d = get_val(perm_d)
    return {p: any(d.get(p) for d in (d, unwrappe_d))
            for p in unwrappe_d}


grouped = groupby(sorted(lod, key=get_key), get_key)

result = [{p: reduce(merge_dicts, perm_dicts, {})}
          for p, perm_dicts in grouped]

This one also depends on all the "inner keys" ('view', 'edit' etc) present. I'll think on how it could be improved.

  • Related