Dado um dicionário como este:
my_map = {'a': 1, 'b': 2}
Como se pode inverter este mapa para obter:
inv_map = {1: 'a', 2: 'b'}
Dado um dicionário como este:
my_map = {'a': 1, 'b': 2}
Como se pode inverter este mapa para obter:
inv_map = {1: 'a', 2: 'b'}
Respostas:
Para Python 2.7.x
inv_map = {v: k for k, v in my_map.iteritems()}
Para Python 3+:
inv_map = {v: k for k, v in my_map.items()}
The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon
. Não há garantia de que continue assim, portanto, não escreva código contando Dict
com o mesmo comportamento de OrderedDict
.
Supondo que os valores no dict sejam únicos:
dict((v, k) for k, v in my_map.iteritems())
iteritems()
será gerada, portanto, pode-se supor que uma chave arbitrária seja atribuída a um valor não exclusivo, de uma maneira que aparentemente seja reproduzível em algumas condições, mas não em geral.
iteritems()
método e essa abordagem não funcionará; use items()
-o como mostrado na resposta aceita. Além disso, uma compreensão do dicionário tornaria isso mais bonito do que chamar dict
.
Se os valores em my_map
não forem exclusivos:
inv_map = {}
for k, v in my_map.iteritems():
inv_map[v] = inv_map.get(v, [])
inv_map[v].append(k)
inv_map.get(v, [])
retorna a lista já adicionada, se houver, para que a atribuição não seja redefinida para uma lista vazia. setdefault
ainda seria mais bonito, no entanto.
inv_map.setdefault(v, set()).add(k)
.
my_map.items()
vez de my_map.iteritems()
.
Para fazer isso, preservando o tipo de seu mapeamento (supondo que seja uma dict
ou uma dict
subclasse):
def inverse_mapping(f):
return f.__class__(map(reversed, f.items()))
Tente o seguinte:
inv_map = dict(zip(my_map.values(), my_map.keys()))
(Observe que os documentos do Python nas visualizações de dicionário garantem explicitamente isso .keys()
e .values()
têm seus elementos na mesma ordem, o que permite que a abordagem acima funcione.)
Alternativamente:
inv_map = dict((my_map[k], k) for k in my_map)
ou usando as compreensões de ditado do python 3.0
inv_map = {my_map[k] : k for k in my_map}
Outra maneira mais funcional:
my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))
filter
e map
deve morrer e ser incluído na compreensão da lista, e não aumentar mais variantes".
dict
com outros tipos de mapeamento, como collections.OrderedDict
oucollections.defaultdict
Isso se expande com a resposta de Robert , aplicada quando os valores no ditado não são únicos.
class ReversibleDict(dict):
def reversed(self):
"""
Return a reversed dict, with common values in the original dict
grouped into a list in the returned dict.
Example:
>>> d = ReversibleDict({'a': 3, 'c': 2, 'b': 2, 'e': 3, 'd': 1, 'f': 2})
>>> d.reversed()
{1: ['d'], 2: ['c', 'b', 'f'], 3: ['a', 'e']}
"""
revdict = {}
for k, v in self.iteritems():
revdict.setdefault(v, []).append(k)
return revdict
A implementação é limitada, pois você não pode usar reversed
duas vezes e obter o original de volta. Não é simétrico como tal. É testado com o Python 2.6. Aqui está um caso de uso de como estou usando para imprimir o ditado resultante.
Se você preferir usar a do set
que a list
, e pode haver aplicativos não ordenados para os quais isso faz sentido, em vez de setdefault(v, []).append(k)
usar setdefault(v, set()).add(k)
.
revdict.setdefault(v, set()).add(k)
set
. É o tipo intrínseco que se aplica aqui. E se eu quiser encontrar todas as chaves onde os valores não estão 1
ou 2
? Então eu posso apenas fazer d.keys() - inv_d[1] - inv_d[2]
(em Python 3) #
Também podemos reverter um dicionário com chaves duplicadas usando defaultdict
:
from collections import Counter, defaultdict
def invert_dict(d):
d_inv = defaultdict(list)
for k, v in d.items():
d_inv[v].append(k)
return d_inv
text = 'aaa bbb ccc ddd aaa bbb ccc aaa'
c = Counter(text.split()) # Counter({'aaa': 3, 'bbb': 2, 'ccc': 2, 'ddd': 1})
dict(invert_dict(c)) # {1: ['ddd'], 2: ['bbb', 'ccc'], 3: ['aaa']}
Veja aqui :
Essa técnica é mais simples e rápida do que uma técnica equivalente
dict.setdefault()
.
Por exemplo, você tem o seguinte dicionário:
dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}
E você quer obtê-lo de forma invertida:
inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}
Primeira solução . Para inverter pares de valores-chave em seu dicionário, use uma for
abordagem -loop:
# Use this code to invert dictionaries that have non-unique values
inverted_dict = dict()
for key, value in dict.items():
inverted_dict.setdefault(value, list()).append(key)
Segunda solução . Use uma abordagem de compreensão de dicionário para inversão:
# Use this code to invert dictionaries that have unique values
inverted_dict = {value: key for key, value in dict.items()}
Terceira solução . Use a reversão da abordagem de inversão (depende da segunda solução):
# Use this code to invert dictionaries that have lists of values
dict = {value: key for key in inverted_dict for value in my_map[key]}
dict
é reservado e não deve ser usado para nomes de variáveis
my_map
é
dictio()
? Você quis dizer dict()
?
Combinação de lista e compreensão de dicionário. Pode lidar com chaves duplicadas
{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}
Se os valores não forem únicos e você for um pouco incondicional:
inv_map = dict(
(v, [k for (k, xx) in filter(lambda (key, value): value == v, my_map.items())])
for v in set(my_map.values())
)
Especialmente para um ditado grande, observe que essa solução é muito menos eficiente que a resposta Python inverte / inverte um mapeamento, porque ele faz um loop items()
várias vezes.
-1
porque ainda responde à pergunta, apenas a minha opinião.
Além das outras funções sugeridas acima, se você gosta de lambdas:
invert = lambda mydict: {v:k for k, v in mydict.items()}
Ou você também pode fazer o seguinte:
invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )
Eu acho que a melhor maneira de fazer isso é definir uma classe. Aqui está uma implementação de um "dicionário simétrico":
class SymDict:
def __init__(self):
self.aToB = {}
self.bToA = {}
def assocAB(self, a, b):
# Stores and returns a tuple (a,b) of overwritten bindings
currB = None
if a in self.aToB: currB = self.bToA[a]
currA = None
if b in self.bToA: currA = self.aToB[b]
self.aToB[a] = b
self.bToA[b] = a
return (currA, currB)
def lookupA(self, a):
if a in self.aToB:
return self.aToB[a]
return None
def lookupB(self, b):
if b in self.bToA:
return self.bToA[b]
return None
Os métodos de exclusão e iteração são fáceis de implementar, se necessários.
Essa implementação é muito mais eficiente do que inverter um dicionário inteiro (que parece ser a solução mais popular nesta página). Sem mencionar, você pode adicionar ou remover valores do seu SymDict o quanto quiser, e seu dicionário inverso sempre permanecerá válido - isso não é verdade se você simplesmente reverter o dicionário inteiro uma vez.
dictresize
, mas essa abordagem nega ao Python essa possibilidade.
Isso lida com valores não exclusivos e mantém grande parte da aparência do caso exclusivo.
inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}
Para Python 3.x, substitua itervalues
por values
.
Função é simétrica para valores da lista de tipos; As tuplas são ocultas em listas ao executar reverse_dict (reverse_dict (dictionary))
def reverse_dict(dictionary):
reverse_dict = {}
for key, value in dictionary.iteritems():
if not isinstance(value, (list, tuple)):
value = [value]
for val in value:
reverse_dict[val] = reverse_dict.get(val, [])
reverse_dict[val].append(key)
for key, value in reverse_dict.iteritems():
if len(value) == 1:
reverse_dict[key] = value[0]
return reverse_dict
Como os dicionários exigem uma chave exclusiva no dicionário, diferentemente dos valores, precisamos anexar os valores revertidos em uma lista de classificação a ser incluída nas novas chaves específicas.
def r_maping(dictionary):
List_z=[]
Map= {}
for z, x in dictionary.iteritems(): #iterate through the keys and values
Map.setdefault(x,List_z).append(z) #Setdefault is the same as dict[key]=default."The method returns the key value available in the dictionary and if given key is not available then it will return provided default value. Afterward, we will append into the default list our new values for the specific key.
return Map
Solução funcional rápida para mapas não-bijetivos (valores não exclusivos):
from itertools import imap, groupby
def fst(s):
return s[0]
def snd(s):
return s[1]
def inverseDict(d):
"""
input d: a -> b
output : b -> set(a)
"""
return {
v : set(imap(fst, kv_iter))
for (v, kv_iter) in groupby(
sorted(d.iteritems(),
key=snd),
key=snd
)
}
Em teoria, isso deve ser mais rápido do que adicionar ao conjunto (ou anexar à lista) um por um, como na solução imperativa .
Infelizmente, os valores precisam ser classificados, a classificação é exigida por groupby.
n
elementos no ditado original, sua abordagem tem O(n log n)
complexidade de tempo devido à necessidade de classificar os itens do ditado, enquanto a abordagem imperativa ingênua tem O(n)
complexidade de tempo. Pelo que sei, sua abordagem pode ser mais rápida, até absurdamente grandedict
na prática , mas certamente não é mais rápida na teoria.
Eu faria assim em python 2.
inv_map = {my_map[x] : x for x in my_map}
dict.items
(ou iteritems
no Python 2) é mais eficiente do que extrair cada valor separadamente enquanto itera as chaves.
def invertDictionary(d):
myDict = {}
for i in d:
value = d.get(i)
myDict.setdefault(value,[]).append(i)
return myDict
print invertDictionary({'a':1, 'b':2, 'c':3 , 'd' : 1})
Isso fornecerá a saída como: {1: ['a', 'd'], 2: ['b'], 3: ['c']}
dict.items
(ou iteritems
no Python 2) é mais eficiente do que extrair cada valor separadamente enquanto itera as chaves. Além disso, você não adicionou nenhuma explicação a uma resposta que duplica outras.
def reverse_dictionary(input_dict):
out = {}
for v in input_dict.values():
for value in v:
if value not in out:
out[value.lower()] = []
for i in input_dict:
for j in out:
if j in map (lambda x : x.lower(),input_dict[i]):
out[j].append(i.lower())
out[j].sort()
return out
este código faz assim:
r = reverse_dictionary({'Accurate': ['exact', 'precise'], 'exact': ['precise'], 'astute': ['Smart', 'clever'], 'smart': ['clever', 'bright', 'talented']})
print(r)
{'precise': ['accurate', 'exact'], 'clever': ['astute', 'smart'], 'talented': ['smart'], 'bright': ['smart'], 'exact': ['accurate'], 'smart': ['astute']}
Não é algo completamente diferente, apenas uma receita reescrita do Cookbook. Além disso, é otimizado pelo setdefault
método de retenção , em vez de obtê-lo através da instância:
def inverse(mapping):
'''
A function to inverse mapping, collecting keys with simillar values
in list. Careful to retain original type and to be fast.
>> d = dict(a=1, b=2, c=1, d=3, e=2, f=1, g=5, h=2)
>> inverse(d)
{1: ['f', 'c', 'a'], 2: ['h', 'b', 'e'], 3: ['d'], 5: ['g']}
'''
res = {}
setdef = res.setdefault
for key, value in mapping.items():
setdef(value, []).append(key)
return res if mapping.__class__==dict else mapping.__class__(res)
Projetado para ser executado no CPython 3.x, para 2.x substitua mapping.items()
pormapping.iteritems()
Na minha máquina roda um pouco mais rápido do que outros exemplos aqui
dict
e depois converter para a classe desejada no final (em vez de começar com uma classe do tipo certo) me parece que incorre em um impacto de desempenho totalmente evitável aqui.
Escrevi isso com a ajuda do ciclo 'for' e do método '.get ()' e mudei o nome 'map' do dicionário para 'map1' porque 'map' é uma função.
def dict_invert(map1):
inv_map = {} # new dictionary
for key in map1.keys():
inv_map[map1.get(key)] = key
return inv_map
Se os valores não forem únicos E puder ser um hash (uma dimensão):
for k, v in myDict.items():
if len(v) > 1:
for item in v:
invDict[item] = invDict.get(item, [])
invDict[item].append(k)
else:
invDict[v] = invDict.get(v, [])
invDict[v].append(k)
E com uma recursão, se você precisar ir mais fundo, apenas uma dimensão:
def digList(lst):
temp = []
for item in lst:
if type(item) is list:
temp.append(digList(item))
else:
temp.append(item)
return set(temp)
for k, v in myDict.items():
if type(v) is list:
items = digList(v)
for item in items:
invDict[item] = invDict.get(item, [])
invDict[item].append(k)
else:
invDict[v] = invDict.get(v, [])
invDict[v].append(k)
{"foo": "bar"}
para {'b': ['foo'], 'a': ['foo'], 'r': ['foo']}
e levanta uma exceção se qualquer valor myDict
não é um iterável. Não tenho certeza de qual comportamento você estava tentando implementar aqui, mas o que você realmente implementou é algo que praticamente ninguém vai querer.
my_map.items()
, também funciona