1

With Python 3.6, what's the most efficient way to transform this dictionary into a List? I've tried to use a lot of loops, but it doesn't look efficient at all, it takes some time. This is the original dictionary:

d = {'Owner': [{'login': 'AAAA', 'mail': '[email protected]'},
               {'login': 'BBBB', 'mail': '[email protected]'},
               {'login': 'CCCC', 'mail': '[email protected]'}],
     'Stakeholder': [{'login': 'DDDD', 'mail': '[email protected]'},
                     {'login': 'AAAA', 'mail': '[email protected]'}],
     'Team': [{'login': 'CCCC', 'mail': '[email protected]'},
              {'login': 'BBBB', 'mail': '[email protected]'}]}

This is the goal:

[{'login': 'AAAA', 'mail': '[email protected]', 'roles': ['Owner', 'Stakeholder']},
 {'login': 'BBBB', 'mail': '[email protected]', 'roles': ['Owner', 'Team']},
 {'login': 'CCCC', 'mail': '[email protected]', 'roles': ['Owner', 'Team']},
 {'login': 'DDDD', 'mail': '[email protected]', 'roles': ['Stakeholder']}]

Thanks!

Edit 1: So far I could get a list of unique users:

list_users = []

for role, users in old_dict.items():
    for user in users:
        list_users.append(user)

unique_list_of_users = []
for i in range(len(list_users)):
    if list_users[i] not in list_users[i + 1:]:
        unique_list_of_users.append(list_users[i])

for user in unique_list_of_users:
    user["role"] = []
2
  • If you've tried loops, you should add them to your question we SO can see what you've tried. Commented Aug 12, 2022 at 11:20
  • I've added some code that I have now Commented Aug 12, 2022 at 11:25

5 Answers 5

1

Try this:

output = []
for k, v in d.items():
    for dct in v:
        for x in output:
            if x['login'] == dct['login']:
                x['roles'].append(k)
                break
        else:
            output.append({**dct, **{'roles': [k]}})
print(output)

Output:

[{'login': 'AAAA', 'mail': '[email protected]', 'roles': ['Owner', 'Stakeholder']},
 {'login': 'BBBB', 'mail': '[email protected]', 'roles': ['Owner', 'Team']},
 {'login': 'CCCC', 'mail': '[email protected]', 'roles': ['Owner', 'Team']},
 {'login': 'DDDD', 'mail': '[email protected]', 'roles': ['Stakeholder']}]

Note: you specified you were on Python 3.6, so {**dct, **{'roles': [k]}} will work for you. It will not work on Python 3.4 or lower. If you are on 3.4 or lower, use:

dct.update({'roles': [k]})
output.append(dct)
Sign up to request clarification or add additional context in comments.

Comments

1

Since you have a key in the login and mail field, you can use those to quickly build a dictionnary with the values you need and then access them.

d = {
    "Owner": [
        {"login": "AAAA", "mail": "[email protected]"},
        {"login": "BBBB", "mail": "[email protected]"},
        {"login": "CCCC", "mail": "[email protected]"},
    ],
    "Stakeholder": [
        {"login": "DDDD", "mail": "[email protected]"},
        {"login": "AAAA", "mail": "[email protected]"},
    ],
    "Team": [
        {"login": "CCCC", "mail": "[email protected]"},
        {"login": "BBBB", "mail": "[email protected]"},
    ],
}

tmp = {}

for k, v in d.items():
    for dd in v:
        try:
            tmp[dd["login"]]["roles"].append(k)
        except KeyError:
            tmp[dd["login"]] = dd
            tmp[dd["login"]].update({"roles": [k]})   

list(tmp.values())

gives

[{'login': 'AAAA', 'mail': '[email protected]', 'roles': ['Owner', 'Stakeholder']},
 {'login': 'BBBB', 'mail': '[email protected]', 'roles': ['Owner', 'Team']},
 {'login': 'CCCC', 'mail': '[email protected]', 'roles': ['Owner', 'Team']},
 {'login': 'DDDD', 'mail': '[email protected]', 'roles': ['Stakeholder']}]

2 Comments

Your current code doesn't produce what you give as the result (I upvoted before noticing this).
@DarrylG good catch, I had not copy/pasted the change for asking permission to asking forgiveness, now I have and it's fixed.
1

Or use collections.defaultdict:

import collections
import pprint
 
d = {
    "Owner": [
        {"login": "AAAA", "mail": "[email protected]"},
        {"login": "BBBB", "mail": "[email protected]"},
        {"login": "CCCC", "mail": "[email protected]"},
    ],
    "Stakeholder": [
        {"login": "DDDD", "mail": "[email protected]"},
        {"login": "AAAA", "mail": "[email protected]"},
    ],
    "Team": [
        {"login": "CCCC", "mail": "[email protected]"},
        {"login": "BBBB", "mail": "[email protected]"},
    ],
}

res = collections.defaultdict(dict)
for k, v in d.items():
    for obj in d[k]:
        if not obj['login'] in res:
            res[obj['login']].update(obj)
        res[obj['login']].setdefault('roles', []).append(k)

pprint.pprint(list(res.values()))

Output:

[{'login': 'AAAA', 'mail': '[email protected]', 'roles': ['Owner', 'Stakeholder']},
 {'login': 'BBBB', 'mail': '[email protected]', 'roles': ['Owner', 'Team']},
 {'login': 'CCCC', 'mail': '[email protected]', 'roles': ['Owner', 'Team']},
 {'login': 'DDDD', 'mail': '[email protected]', 'roles': ['Stakeholder']}]

4 Comments

One caveat though - this produces final output with the "last" mail key - the OP's expected output wants the "first" mail key
@Mortz What do you mean? Not sure I understand
For login: AAAA the expected output for mail is mail: [email protected] (5 As) - which comes from Owner - whereas your output is mail: [email protected] (4 As) - which comes from Stakeholder
@Mortz Nice catch! I've fixed that
0

Here is another solution using itertools -

from itertools import chain, groupby
flattened_items = sorted(chain.from_iterable([list(zip([k]*len(v), v)) for item in d.items() for k, v in (item,)]), key=lambda x: x[1]['login'])

for k, v in groupby(flattened_items, key=lambda x: x[1]['login']):
    all_roles, all_dicts = zip(*v)
    new_dict = {kk: vv for d in reversed(all_dicts) for kk, vv in d.items()}
    new_dict['roles'] = list(all_roles)
    print(new_dict)

What I am doing is to flatten the d.items() into (key, value) pairs, sort it by the values and then aggregate using itertools.groupby

Output

{'login': 'AAAA', 'mail': '[email protected]', 'roles': ['Owner', 'Stakeholder']}
{'login': 'BBBB', 'mail': '[email protected]', 'roles': ['Owner', 'Team']}
{'login': 'CCCC', 'mail': '[email protected]', 'roles': ['Owner', 'Team']}
{'login': 'DDDD', 'mail': '[email protected]', 'roles': ['Stakeholder']}

Comments

0

I believe the most optimal way is to let your dicts that belong to the list of each role hashable. Then, you can make them be the key of another dict that will tell the roles of each login+mail dict. The python code would be:

class Hashabledict(dict):
    def __hash__(self):
        return hash(frozenset(self))


def solve(d):
    roles = dict()

    for key, value in d.items():
        for cur_dict in value:
            hashable_cur_dict = Hashabledict(cur_dict)

            if hashable_cur_dict not in roles:
                roles[hashable_cur_dict] = {key}
            else:
                roles[hashable_cur_dict].add(key)

    answer = list()

    for key, value in roles.items():
        key['roles'] = list(value)
        answer.append(key)

    return answer

An example of use is below:

if __name__ == "__main__":
    d = {'Owner': [{'login': 'AAAA', 'mail': '[email protected]'},
                   {'login': 'BBBB', 'mail': '[email protected]'},
                   {'login': 'CCCC', 'mail': '[email protected]'}],
         'Stakeholder': [{'login': 'DDDD', 'mail': '[email protected]'},
                         {'login': 'AAAA', 'mail': '[email protected]'}],
         'Team': [{'login': 'CCCC', 'mail': '[email protected]'},
                  {'login': 'BBBB', 'mail': '[email protected]'}]}

    test = solve(d)
    print(test)

The output is:

[{'login': 'AAAA', 'mail': '[email protected]', 'roles': ['Owner', 'Stakeholder']}, {'login': 'BBBB', 'mail': '[email protected]', 'roles': ['Owner', 'Team']}, {'login': 'CCCC', 'mail': '[email protected]', 'roles': ['Owner', 'Team']}, {'login': 'DDDD', 'mail': '[email protected]', 'roles': ['Stakeholder']}]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.