Reordering of dictionary, exchange values with keys

4

I have the following dictionary:

tests_dict = {'test 1': ['Manuel', 'Mariana', 'Filipa'], 'test 2': ['Manuel', 'Filipa', 'Mariana'], 'test 3': ['Mariana', 'Manuel', 'Filipa']}

What I need is that the keys of the new dictionary are the names of the people, with the test done and the place (index) where the respective name is in the current dictionary list, the desired format:

peps_dict = {'Manuel': {'test 1': 1, 'test 2': 1, 'test 3': 2}, 'Mariana': {'test 1': 2, 'test 2': 3, 'test 3': 1}, 'Filipa': {'test 1': 3, 'test 2': 2, 'test 3': 3}}

As you can see, Manuel in test 1 was in first in test 2 also was in first and in test 3 was in second, these positions are based on the index of the lists in the original dictionary, and add 1, because it is a rank system and there can not be below the first place (0 does not make sense)

    
asked by anonymous 11.12.2016 / 10:19

1 answer

5

Then we have:

tests_dict = {'test 1': ['Manuel', 'Mariana', 'Filipa'], 'test 2': ['Manuel', 'Filipa', 'Mariana'], 'test 3': ['Mariana', 'Manuel', 'Filipa']}

You can do this:


1.

This first way is more didactic, I think it perceives the logic behind this:
peps_dict = {}
for test in tests_dict:
    for indx, name in enumerate(tests_dict[test], 1): # vamos percorrer os nomes e respetivo indice em cada lista, começando em 1
        if name not in peps_dict: # se o nome ainda nao estiver presente como chave no nosso novo dionario
            peps_dict[name] = {} # vamos declara-lo como um dicionario tambem
        peps_dict[name][test] = indx # ex: peps_dict['Manuel']['test 1'] = 1, {'Manuel': {'test 1': 1}}

DEMONSTRATION


2.

In this way we will import defaultdict , which in this case allows us to exclude the check if name not in peps_dict , in this way there is no KeyError in our new dictionary, and each key (name) by default we define a dictionary as well:
from collections import defaultdict

peps_dict = defaultdict(dict)
for test in tests_dict:
    for indx, name in enumerate(tests_dict[test], 1):
        peps_dict[name][test] = indx

DEMO

An alternative, in this case, to defaultdict is to use setdefault (Thanks to @JJao that reminded me of this in comment), and therefore also excludes import , this method is applicable only to dictionaries:

peps_dict = {}
for test in tests_dict:
    for indx, name in enumerate(tests_dict[test], 1):
        peps_dict.setdefault(name,{})[test] = indx

DEMO


3.

This last one did more for the challenge, the logic changes a bit in relation to the first. Let's go through all the names, put all the names together into one list ( (name for listN in tests_dict.values() for name in listN) ), generator in this case, and we added every n as a key to our dictionary, and then via dict compreension populamos them with the corresponding values ({ test: tests_dict[test].index(n) + 1} , we will get the index that the name has in the original list and we add 1)
peps_dict = {}
names = (name for listN in tests_dict.values() for name in listN)
for n in names:
    peps_dict[n] = {test: tests_dict[test].index(n) + 1 for test in tests_dict if n in tests_dict[test]}

DEMONSTRATION

The latter can be reduced to:

names = (name for listN in tests_dict.values() for name in listN)
peps_dict = {n: {test: tests_dict[test].index(n) + 1 for test in tests_dict if n in tests_dict[test]} for n in names}

DEMONSTRATION

Output ( print(peps_dict) ) for the above cases:

  

'' '' '' '' '' '' '' '' '' '' '' '' '' '' 'test 3': 3, 'test 3': 1}}

PS: All this exercise is based on the assumption that there are no equal names in each list, if there are two "Manuel" in a list, the last one to be traversed will be superimposed on the first one, strong>

    
11.12.2016 / 10:23