Reputation: 2564
I have two dictionaries:
source = {'a': 10, 'b': 20, 'c': 30}
destination = {'x': None, 'y': None, 'z': None, 'qq': 'Some value'}
I would like to map the source dictionary to the destination, based on some pre-set mapping, e.g.:
a -> y
b -> z
c -> x
I could create a function that loops over the destination dictionary and assigns the appropriate value based on a nested if:
for k, v in destination.items():
if k == 'x':
destination[k] = source['c']
elif k == 'y':
destination[k] = source['a']
elif k == 'z':
destination[k] = source['b']
This doesn't seem very scalable.
Additionally I might have nested keys in both the source and destination, which complicates the loop even more.
An example of a nested source/destination dictionary might be:
source_nest =
{
"a": {
"f": {
"k": 4,
"l": "cat"
}
},
"b": {
"s": "hit"
}
}
destination_nest =
{
"x": None,
"y": {
"q": None,
"r": 100
},
"z": None
}
With an example mapping like:
a/f/k -> y/q
a/f/l -> x
b/s -> z
What would be a more efficient way to do this re-mapping?
If it matters, these dictionaries are representations of JSON files.
Upvotes: 1
Views: 72
Reputation: 4253
I used the value of the mapping as the key to the source and created a key/value pair dictionary called result
source = {'a': 10, 'b': 20, 'c': 30}
destination = {'x': None, 'y': None, 'z': None, 'qq': 'Some value'}
mapping = {"y": "a", "z": "b", "x": "c"}
result={k:source.get(v) for (k,v) in mapping.items()}
print(result)
output:
{'y': 10, 'z': 20, 'x': 30}
Upvotes: 0
Reputation: 82939
Define the mapping as another dictionary with keys and values inversed, then use a dictionary comprehension with ternary if/else to get the result:
source = {'a': 10, 'b': 20, 'c': 30}
destination = {'x': None, 'y': None, 'z': None, 'qq': 'Some value'}
mapping = {"y": "a", "z": "b", "x": "c"} # note: key/values inversed
result = {k: source[mapping[k]] if k in mapping else v
for k, v in destination.items()}
# {'x': 30, 'y': 10, 'z': 20, 'qq': 'Some value'}
Or map source
to the destination
keys first, then use get
with default:
mapping = {"a": "y", "b": "z", "c": "x"} # note: original order
source_mapped = {mapping.get(k, k): v for k, v in source.items()}
result = {k: source_mapped.get(k, v) for k, v in destination.items()}
For the nested case, you may first have to define two recursive functions, let's call them deep_set
and deep_get
to set and get values in a nested dictionary using a multi-part key.
def deep_set(d, keys, val):
first, *rest = keys
if rest:
deep_set(d[first], rest, val)
else:
d[first] = val
def deep_get(d, keys):
first, *rest = keys
if rest:
return deep_get(d[first], rest)
else:
return d[first]
Then you can again define the mapping, iterate the pairs, and replace them in the destination directory. This does not work as well with a dict comprehension, so I'll just modify the destination directory; make sure to create a deep copy first if you still need it.
mapping = [("a/f/k", "y/q"), ("a/f/l", "x"), ("b/s", "z")]
for s, t in mapping:
val = deep_get(source_nest, s.split("/"))
deep_set(destination_nest, t.split("/"), val)
# {'x': 'cat', 'y': {'q': 4, 'r': 100}, 'z': 'hit'}
Upvotes: 3