Mapping two datastructures consisting of lists and dictionaries. The mapping from data should be recursively applied to the payload structure.
These are my inputs
data = {
'a': 'Apple',
'b': 'Ball',
'c': 'Cat',
'd': 'Dog',
'e': 'Egg',
'f': 'Fish',
'g': 'Goat',
'h': 'House',
'i': 'Ice-Cream',
'j': 'Jaguar',
'k': 'Key',
'l': 'Lock',
'm': 'Map'
}
and
payload = {
'PI': [
{
'one': 'a',
'two': 'b',
'three': 'c',
'four': {
'five': 'd',
'six': 'e',
'seven': 'f',
'eight': 'g'
}
}, {
'nine': 'h',
'ten': 'i',
'eleven': 'j',
'twelve': 'k'
}
]
}
Expected output:
payload = {
'PI': [
{
'one': 'Apple',
'two': 'Ball',
'three': 'Cat',
'four': {
'five': 'Dog',
'six': 'Egg',
'seven': 'Fish',
'eight': 'Goat'
}
}, {
'nine': 'House',
'ten': 'Ice-Cream',
'eleven': 'Jaguar',
'twelve': 'Key'
}
]
}
This my attempt at creating the mapping but it is not working
def mapping(payload):
for k,v in payload.items():
if(isinstance(v,dict)):
mapping(v)
elif(isinstance(v,list)):
for item in v:
mapping(item)
else:
try:
v = data[v]
except KeyError:
pass
return payload
I get this instead:
{
'PI': [
{
'four': {
'eight': 'g',
'five': 'd',
'seven': 'f',
'six': 'e'
},
'one': 'a',
'three': 'c',
'two': 'b'
},
{
'eleven': 'j',
'nine': 'h',
'ten': 'i',
'twelve': 'k'
}
]
}
Nothing has been replaced.
You can indeed use recursion; recurse for list values, and dictionary elements, and just map directly for everything else. Do not forget to return the result; the recursive function creates new dictionaries and lists:
def replace_strings(value, mapping):
if isinstance(value, list):
return [replace_strings(v, mapping) for v in value]
if isinstance(value, dict):
return {replace_strings(k, mapping): replace_strings(v, mapping)
for k, v in value.items()}
return mapping.get(value, value)
Demo:
>>> pprint(replace_strings(payload, data))
{'PI': [{'four': {'eight': 'Goat',
'five': 'Dog',
'seven': 'Fish',
'six': 'Egg'},
'one': 'Apple',
'three': 'Cat',
'two': 'Ball'},
{'eleven': 'Jaguar',
'nine': 'House',
'ten': 'Ice-Cream',
'twelve': 'Key'}]}
Your code had several issues:
You ignored the return value of any recursive call.
You returned the original input, unaltered.
Related
I have a list of dictionaries, and some of them are subsets:
l = [
{'zero': 'zero', 'one': 'example', 'two': 'second'},
{'zero': 'zero', 'one': 'example', 'two': 'second', 'three': 'blabla'},
{'zero': 'zero'},
{'zero': 'non-zero', 'one': 'example'}, ...
]
And I want to create a new list of dictionaries that do not contain a subset of dictionaries.
res = [
{'zero': 'zero', 'one': 'example', 'two': 'second', 'three': 'blabla'},
{{'zero': 'non-zero', 'one': 'example'}, ...
]
This work around will create a new list that only contains dictionaries that are not subsets of any other dictionary in the list
res = [
d for d in l
if not any(set(d.items()).issubset(set(other.items()))
for other in l if other != d)
]
print(res)
Output:
[{'zero': 'zero', 'one': 'example', 'two': 'second', 'three': 'blabla'},
{'zero': 'non-zero', 'one': 'example'}]
I have a dictionary like this
{'C':
{
'G':
{
'Z': '4', 'L': '1', 'P': [{'K': '3', 'B': '1'}, {'K': '7', 'B': '9'}]
}
}
}
and I'm trying to convert to list each value that is a dictionary in order to get a new dictionary like this
{'C':
[{
'G':
[{
'Z': '4', 'L': '1', 'P': [{'K': '3', 'B': '1'}, {'K': '7', 'B': '9'}]
}]
}]
}
To see it easely would be pass from dict1 to dict2
My attempt so far is like this. Thanks in advance.
>>> for k, v in d1.items():
... if isinstance(v, dict):
... d1[k,v] = d1[k,[v]]
...
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
TypeError: unhashable type: 'list'
You can use recursion:
def to_list(d):
if not isinstance(d, (dict, list)):
return d
if isinstance(d, list):
return list(map(to_list, d))
return {a:to_list(b) if not isinstance(b, dict) else [to_list(b)] for a, b in d.items()}
vals = {'C': {'G': {'Z': '4', 'L': '1', 'P': [{'K': '3', 'B': '1'}, {'K': '7', 'B': '9'}]}}}
r = to_list(vals)
Output:
{'C': [{'G': [{'Z': '4', 'L': '1', 'P': [{'K': '3', 'B': '1'}, {'K': '7', 'B': '9'}]}]}]}
If you want your key to be k then you don't want d1[k,v], rather you should set d1[k] = [v]
Note that you have not provided a way to go deeper into the dictionary, this will only convert top-level values that are dicts to lists containing the dict. You'll need a way to recurse into those dicts if you wish to convert any dicts that they contain as values.
You can try recursion:
dct = {
"C": {
"G": {
"Z": "4",
"L": "1",
"P": [{"K": "3", "B": "1"}, {"K": "7", "B": "9"}],
}
}
}
def convert(d):
if isinstance(d, dict):
out = {}
for k, v in d.items():
out[k] = [convert(v)] if isinstance(v, dict) else convert(v)
return out
elif isinstance(d, list):
return [convert(v) for v in d]
return d
print(convert(dct))
Prints:
{
"C": [
{
"G": [
{
"Z": "4",
"L": "1",
"P": [{"K": "3", "B": "1"}, {"K": "7", "B": "9"}],
}
]
}
]
}
Recursive approach
d = {'C':
{
'G':
{
'Z': '4', 'L': '1', 'P': [{'K': '3', 'B': '1'}, {'K': '7', 'B': '9'}]
}
}
}
def d2l(d):
for k, v in d.items():
if isinstance(v, dict):
d2l(v)
d[k] = [v]
return d
new_dict = d2l(d)
print(new_dict)
Output
{'C': [{'G': [{'Z': '4', 'L': '1', 'P': [{'K': '3', 'B': '1'}, {'K': '7', 'B': '9'}]}]}]}
I would use recursion, this seemed to work for me:
main = {
'C': {
'G': {
'Z': '4',
'L': '1',
'P': [{'K': '3', 'B': '1'},
{'K': '7', 'B': '9'}]
}
}
}
def convert_dictionary(dictionary: dict):
for key, value in dictionary.items():
if isinstance(value, dict):
convert_dictionary(value)
dictionary[key] = [value]
convert_dictionary(main)
print(main)
Tell me if it helped!
Yet another recursion solution (added a third level)
def toList(d, d2):
for k, v in d.items():
if isinstance(v, dict):
d2[k] = [v]
toList(v, d2[k][0])
d1 = {'C':
{
'G':
{
'Z': '4', 'L': '1', 'P': [{'K': '3', 'B': '1'}],
"thirdLvl": {'K': '7', 'B': '9'}
}
}
}
d2 = {}
toList(d1,d2)
print(d2)
Result:
{'C':
[{'G':
[{'Z': '4', 'L': '1', 'P': [{'K': '3', 'B': '1'}],
'thirdLvl': [{'K': '7', 'B': '9'}]
}
]
}
]
}
I have a dictionary which I'd like to create a key/value pair where the value is the same as a previous property.
Is there a way I can assign property2's value to be the same as property1 without repeating myself?
obj = {
'property1': 'abc',
'property2': 'abc'
}
I tried this:
obj = {
'property1': 'abc',
'property2': obj['property1']
}
But that is referencing obj before it's assigned.
u can store the value in a variable first and then assign it like:
value = 'abc'
obj = {
'property1': value,
'property2': value
}
If you want to assign a value to a dictionary, you can do it in a few ways:
List Comprehension:
d = {a:'abc' for a in list('abcdefghijk')}
This will assign 'abc' to all keys in the dictionary.
The output of this will be:
{'a': 'abc', 'b': 'abc', 'c': 'abc', 'd': 'abc', 'e': 'abc', 'f': 'abc', 'g': 'abc', 'h': 'abc', 'i': 'abc', 'j': 'abc', 'k': 'abc'}
Assign to variable:
(like Marcos highlighted in earlier post)
value = 'abc'
d = {a:value for a in list('abcdefghijk')}
Same result:
{'a': 'abc', 'b': 'abc', 'c': 'abc', 'd': 'abc', 'e': 'abc', 'f': 'abc', 'g': 'abc', 'h': 'abc', 'i': 'abc', 'j': 'abc', 'k': 'abc'}
I have the following flatten dictionary containing one entry for each item and each item contains a parent and children attribute.
{
'a': {
parent: None,
children: ['b', 'c', 'd']
},
'b': {
parent: 'a',
children: ['e', 'f', 'g']
},
'c': {
parent: 'a',
children: []
},
'd': {
parent: 'a',
children: []
},
'e': {
parent: 'b',
children: []
},
'f': {
parent: 'b',
children: ['h']
},
'g': {
parent: 'b',
children: []
},
'h': {
parent: 'f',
children: []
},
}
How can I turn it into a nested dictionary which looks like this?
{
'a': {
'b': {
'e': {},
'f': {
'h':
}
'g': {}
},
'c': {},
'd': {},
}
}
You can use recursion:
d = {'a': {'parent': None, 'children': ['b', 'c', 'd']}, 'b': {'parent': 'a', 'children': ['e', 'f', 'g']}, 'c': {'parent': 'a', 'children': []}, 'd': {'parent': 'a', 'children': []}, 'e': {'parent': 'b', 'children': []}, 'f': {'parent': 'b', 'children': ['h']}, 'g': {'parent': 'b', 'children': []}, 'h': {'parent': 'f', 'children': []}}
def group(start=None):
return {a:group(a) for a, b in d.items() if b['parent'] == start}
import json
print(json.dumps(group(), indent=4))
Output:
{
"a": {
"b": {
"e": {},
"f": {
"h": {}
},
"g": {}
},
"c": {},
"d": {}
}
}
I have the following data structure: {'one':['a','b','c'],'two':['q','w','e'],'three':['t','u','y'],...}. So, the dictionary has variant count of keys. Each array, picked by dict's key has similar length. How I can convert this structure to following: [{'one':'a','two':'q','three':'t'},{'one':'b','two':'w','three':'y'},...]?
I think I should use itertools.izip(), but how i can apply it with not predefined count of args? Maybe something like this: itertools.izip([data[l] for l in data.keys()])?
TIA!
Not terribly elegant, but does the trick:
In [9]: [{k:v[i] for (k,v) in d.items()} for i in range(len(d.values()[0]))]
Out[9]:
[{'one': 'a', 'three': 't', 'two': 'q'},
{'one': 'b', 'three': 'u', 'two': 'w'},
{'one': 'c', 'three': 'y', 'two': 'e'}]
I can't help thinking that there's got to be a better way to phrase the i loop, but nothing comes to mind right now.
Alternatively:
In [50]: map(dict, zip(*[[(k, v) for v in l] for k, l in d.items()]))
Out[50]:
[{'one': 'a', 'three': 't', 'two': 'q'},
{'one': 'b', 'three': 'u', 'two': 'w'},
{'one': 'c', 'three': 'y', 'two': 'e'}]
Not sure if this is much of an improvement on the readability front though.
Your assessment in using izip is correct but the way of using it is not quite right
You first need to
get the items as a list of tuples (key, value), (using iteritems() method if using Py2.x or items() if using Py3.x)
create a scalar product of key and value
flatten the list (using itertools.chain)
zip it (using itertools.izip)
and then create a dict of each element
Here is the sample code
>>> from pprint import PrettyPrinter
>>> pp = PrettyPrinter(indent = 4)
>>> pp.pprint(map(dict, izip(*chain((product([k], v) for k, v in data.items())))))
[ { 'one': 'a', 'three': 't', 'two': 'q'},
{ 'one': 'b', 'three': 'u', 'two': 'w'},
{ 'one': 'c', 'three': 'y', 'two': 'e'}]
>>>