Find and change specific Python dictionary

Asked

Viewed 105 times

3

I have to perform a search inside a list with dictionaries inside, looking for the person’s specific CPF, to confirm their data, and thus change some specific dictionary item. My list is:

cadastro_mec = [
    {'CPF': '123.456.789-10', 'Nome': 'Guilherme Flavio', 'Data de Nascimento': '01/04/1989', 'Salário': 'R$1045.0',
     'E-mail': '[email protected]', 'Telefone': '(19) 89765-4326'},
    {'CPF': '123.456.789-11', 'Nome': 'Marco Machado', 'Data de Nascimento': '09/11/1990', 'Salário': 'R$1648.0',
     'E-mail': '[email protected]', 'Telefone': '(11) 3665-4899'}]

The code I made so far was:

for dic in lista:
        for cont, (key, value) in enumerate(dic.items()):
            if confirmar_cpf == value:
                print(f'{cont} -> {key}: {value}') #Aqui eu queria mostrar todos os dados do dicionário, para que o usuário possa escolher qual dado ele deseja alterar especificamente
                break
            else:
                print('CPF NÃO ENCONTRADO!')
                break

However, when executing the code, I get this:

CPF: 12345678910 #input
123.456.789-10
0 -> CPF: 123.456.789-10
CPF NÃO ENCONTRADO!

I get the message that the CPF was not found, and the same is in one of the dictionaries. In addition, not all Keys and values of the specific dictionary are shown.

3 answers

3

To understand why your code doesn’t work, just make a table test, but anyway, let’s understand what happens.

The break'Those you put in interrupt the for more internal (what is iterating through the dictionary keys), then the for external will continue to iterate through all dictionaries. That is to say:

for dic in lista: # primeiro for
    for cont, (key, value) in enumerate(dic.items()): # segundo for
        if confirmar_cpf == value:
            print(f'{cont} -> {key}: {value}')
            break # este break interrompe o segundo for
        else:
            print('CPF NÃO ENCONTRADO!')
            break  # este break interrompe o segundo for

That is, in the first iteration of the first for, the variable dic will be the dictionary containing the CPF 123.456.789-10. Then he enters the second for, iterating through its keys, and finds the CPF (i.e., enters the if, prints the CPF and the break interrupts the second for, then he stops iterating for the keys to the dictionary).

But the break just interrupted the loop internal, however the loop not external, so it continues to iterate through the list. There in the second iteration dic will be the dictionary containing the CPF 123.456.789-11. The second for iterates by the keys of this, in the first iteration sees that the value is not equal to the CPF being searched and enters the else, printing "CPF NOT FOUND" message and the break interrupts the for intern.

And since the list only has two elements, the for external closes.


If the idea is to compare a specific key, do not need to iterate through all, just access it directly (in case, it would be dic['CPF'] to access the CPF value directly). And only if the CPF is found, then you iterate for the CPF keys:

cadastro_mec = [
    {'CPF': '123.456.789-10', 'Nome': 'Guilherme Flavio', 'Data de Nascimento': '01/04/1989', 'Salário': 'R$1045.0',
     'E-mail': '[email protected]', 'Telefone': '(19) 89765-4326'},
    {'CPF': '123.456.789-11', 'Nome': 'Marco Machado', 'Data de Nascimento': '09/11/1990', 'Salário': 'R$1648.0',
     'E-mail': '[email protected]', 'Telefone': '(11) 3665-4899'}]

confirmar_cpf = '123.456.789-10'

for dic in cadastro_mec:
    if confirmar_cpf == dic['CPF']: # acessar o CPF diretamente
        # se encontrou o CPF, aí sim imprime todas as chaves do dicionário
        for cont, (key, value) in enumerate(dic.items()):
            print(f'{cont} -> {key}: {value}')
        break # interrompe o for externo
else:
    print('CPF NÃO ENCONTRADO!')

Note that only if the number is found, I do the other for iterating through the dictionary keys. And inside the if has the break, which in this case interrupts the for external (because if I have already found the CPF, I no longer need to iterate through the rest of the list).

And the else is from for, and not of if (yes, in Python this is possible). In this case, he enters the else if the for nay is interrupted by a break. And how I only use break if you find the number, then it only goes into the else if the number is not found.

3

If the list of entries is a short list and only one search being done in this list the other solutions in linear time, where the codes iterate all the data at each query, solve your problem.

But if the list is long, for example a list containing a million entries and if there is a need for successive searches would make a solution in linear time, O(n), impracticable as every query this list will be traversed item by item testing them until you find the one whose key CPF is equal to the value sought making the process inefficient with respect to processing time.

One possibility to perform more performative searches on robust data is to create a index which is a ruse that improves the speed of recovery operations in the data source at the cost of space write in memory of a second data structure designed to minimize data access time.

In Python a simple way to create indexes for a list of dictionaries whose keys values are unique (not repeated) and which are hasheable is using a scatter table which is a data structure, which associates search keys to values in constant time. In Python the scatter table is implemented as a dictionary.

Example:

cadastro_mec = []                #Lista que armazenará os cadastros.
indice_cpf = {}                  #Dicionário que funcionará como índice para cadastro_mec 
     
print("Aguarde o carregamento dos dados...")
# Gera um milhão de cadastros de teste na memória com CPFs(fakes) variando de 000.000.000-00 até 000.009.999-99
for r in range(1000000):
    cpf = f'{r:011d}'
    cpf = f'{cpf[0:3]}.{cpf[3:6]}.{cpf[6:9]}-{cpf[-2:]}'
    novo = {'CPF': cpf, 'Nome': 'xxxxx xxxx', 'Data de Nascimento': '01/01/1900', 'Salário': 'R$0.0',
     'E-mail': '[email protected]', 'Telefone': '(xx) xxxxxxx'}
    cadastro_mec.append(novo)     #Adiciona a lista cadastro_mec a referência a um novo cadastro.
    indice_cpf[cpf] = novo        #Indexa, na chave cujo é numero do cpf, a referência ao cadastro recém criado.

while True:
    confirmar_cpf = input('Digite um CPF para pesquisa ou enter para sair: ' )
    if confirmar_cpf == "": break
    #Testa se o dicionario indice_cpf contém a chave inserida pelo usuário...
    if confirmar_cpf in indice_cpf:
        print(indice_cpf[confirmar_cpf])     #Se a chave existir imprime o seu valor.
        #indice_cpf[confirmar_cpf]['Nome'] = `Ultimo bom nome` #<-- Exemplo prático de como mudar os valores no cadastro referenciado por meio do índice.
    else: 
        print('CPF não cadastrado.')         #Se a chave existir emite um aviso.

The above algorithm works initially by defining the data structures cadastro_mec which is the list that will contain the records and indice_cpf which will be the index for data stored in cadastro_mec.
Then create one million test(fakes) records and each created element indexes.

Initializes an indeterminate duration loop, where the user inserts the number of a CPF and in constant time, O(1), is verified in the index its existence and printing the registration if this is located.

2

You don’t need the loop for more internal. Here is a simpler solution:

cadastro_mec = [
{'CPF': '123.456.789-10', 'Nome': 'Guilherme Flavio', 'Data de Nascimento': '01/04/1989', 'Salário': 'R$1045.0',
 'E-mail': '[email protected]', 'Telefone': '(19) 89765-4326'},
{'CPF': '123.456.789-11', 'Nome': 'Marco Machado', 'Data de Nascimento': '09/11/1990', 'Salário': 'R$1648.0',
 'E-mail': '[email protected]', 'Telefone': '(11) 3665-4899'}]

for dic in cadastro_mec:
    if confirmar_cpf == dic["CPF"]:
        print(f'CPF: {dic["CPF"]}')
        break
else:
    print('CPF NÃO ENCONTRADO!')

The for has a clause else. If the loop is fully traversed the condition else of the loop for is triggered

Browser other questions tagged

You are not signed in. Login or sign up in order to post.