Using tuple as key for nested properties with json.dump - python

Not sure how to use a tuple as a set of strings the way I would like.
I would like my json to look like:
'item': {
'a': {
'b': {
'c': 'somevalue'
}
}
}
Which could be done with:
item = {}
item['a']['b']['c'] = "somevalue"
However a, b, and c are dynamic, so I understand I need to use a tuple, but this does not do what I would like:
item = {}
path = ('a','b','c')
item[path] = "somevalue"
json.dump(item, sys.stdout)
So I am getting the error:
TypeError("key " + repr(key) + " is not a string"
How do I dynamically get item['a']['b']['c']?

AFAIK there are no builtins for this task, so you need to write a couple of recursive functions:
def xset(dct, path, val):
if len(path) == 1:
dct[path[0]] = val
else:
if path[0] not in dct: dct[path[0]] = {}
xset(dct[path[0]], path[1:], val)
def xget(dct, path):
if len(path) == 1:
return dct[path[0]]
else:
return xget(dct[path[0]], path[1:])
Usage:
>>> d = {}
>>> xset(d, ('a', 'b', 'c'), 6)
>>> d
{'a': {'b': {'c': 6}}}
>>> xset(d, ('a', 'b', 'd', 'e'), 12)
>>> d
{'a': {'b': {'c': 6, 'd': {'e': 12}}}}
>>> xget(d, ('a', 'b', 'c'))
6

Try this:
item = {}
for i in reversed(path):
tmp = {**item}
item ={}
item[i] = {**tmp} if path.index(i)!=len(path)-1 else 'somevalue'

Related

How to cast a list to a dictionary

I have a list as a input made from tuples where the origin is the 1st object and the neighbour is the 2nd object of the tuple.
for example :
inp : lst = [('a','b'),('b','a'),('c',''),('a','c')]
out : {'a': ('a', ['b', 'c']), 'b': ('b', ['a']), 'c': ('c', [])}
first i tried to cast the list into a dictonary,
like this
dictonary = dict(lst)
but i got an error say that
dictionary update sequence element #0 has length 1; 2 is required
The simplest is probably inside a try / except block:
lst = [('a','b'),('b','a'),('c',''),('a','c')]
out = dict()
for k, v in lst:
try:
if v != '':
out[k][1].append(v)
else:
out[k][1].append([])
except KeyError:
if v != '':
out[k] = (k, [v])
else:
out[k] = (k, [])
print out
Which gives:
{'a': ('a', ['b', 'c']), 'b': ('b', ['a']), 'c': ('c', [])}
Here's how I did it, gets the result you want, you can blend the two operations into the same loop, make a function out of it etc, have fun! Written without Python one liners kung-fu for beginner friendliness!
>>> lst = [('a','b'),('b','a'),('c',''),('a','c')]
>>> out = {}
>>> for pair in lst:
... if pair[0] not in out:
... out[pair[0]] = (pair[0], [])
...
>>> out
{'a': ('a', []), 'c': ('c', []), 'b': ('b', [])}
>>> for pair in lst:
... out[pair[0]][1].append(pair[1])
...
>>> out
{'a': ('a', ['b', 'c']), 'c': ('c', ['']), 'b': ('b', ['a'])}
Just here to mention setdefault
lst = [('a','b'),('b','a'),('c',''),('a','c')]
d = {}
for first, second in lst:
tup = d.setdefault(first, (first, []))
if second and second not in tup[1]:
tup[1].append(second)

how to extend a python dictionary while there are list inside

I have tried to fetch data from a dictionary like mongodb do.
while there is a document like
{'a': 'b', 'c': ['d', {'e': ['i', {'j': 'h'}]}]}
that I want to get the value 'h' from the search string like 'c.e.j' as mongodb will do
> db.cc.findOne({'c.e.j':'h'})
{
"_id" : ObjectId("551e5047342b12656b4edecc"),
"a" : "b",
"c" : [
"d",
{
"e" : [
"i",
{
"j" : "h"
}
]
}
]
}
so the first time I think is I will need to extend the dictionary for each list inside, and I have been coding for 4 hours, buy I think it is hard for me.
The final version of my code looks like this:
import types
def extdic(cc):
lst = []
for key in cc:
value = cc[key]
if type(value) == types.DictType:
for _ in extdic(value):
cc[key] = _
lst.append(eval(repr(cc)))
return lst
if type(value) == types.ListType:
#print cc
for _ in value:
cc[key] = _
lst.append(eval(repr(cc)))
#print lst
return lst
else:
return [cc]
def mkdic(cc):
lst = []
if type(cc) == types.ListType:
lst = cc
else:
lst = [cc]
reslst = []
while True:
for _ in lst:
#print _
ext = extdic(_)
#print ext
reslst = reslst + ext
if len(reslst) == len(lst):
break
else:
lst = reslst
reslst = []
return lst
if __name__ == '__main__':
cc = [
{'a': 'b', 'c': ['d', {'e': ['i', 'j']}]},
{'f':['g','h']}
]
cd = {'a': 'b', 'c': {'e': ['i', 'j']}}
ce = {'a': {'b': {'c':{'d':{'e':['f','g']}}}}}
for _ in mkdic(cc):
print _
It is sad that I still cannot get what I want
I only get the 'ce' dictionary work like
MBA:code cc$ python todic3.py
{'a': {'b': {'c': {'d': {'e': 'f'}}}}}
{'a': {'b': {'c': {'d': {'e': 'g'}}}}}
MBA:code cc$
other dictionary struct still not the thing I want..
MBA:code cc$ python todic3.py
{'a': 'b', 'c': ['d', {'e': ['i', {'j': 'h'}]}]}
{'f': 'g'}
{'f': 'h'}
MBA:code cc$
I want to use the tools like
MBA:code cc$ echo "{'a': 'b', 'c': ['d', {'e': ['i', {'j': 'h'}]}]}" | python todic3.py c.e.j
c.e.j: h
MBA:code cc$
help please..
thank you very much
This should return the specified value. The 'yield from' statements require that you use Python 3.
Unfortunately I cannot test it ATM.
EDIT: I tested it just now, turns out there where some significant bugs. I fixed them and it works as intended now.
def iter_flattened(data):
'''returns a generator that allows iteration over key,value pairs, even if the dictionaries are nested in lists'''
if isinstance(data, dict):
yield from data.items()
elif isinstance(data, list):
for item in data:
yield from iter_flattened(item)
def _find(data, keys):
'''This function searches the given (sub-)tree for the given keys'''
if len(keys) == 0:
return data
for key, value in iter_flattened(data):
if key == keys[0]:
result = _find(value, keys[1:])
else:
result = _find(value, keys)
if result is not None:
return result
return None
def find(data, path):
'''Interface function, that accepts the keys as a string seperated with dots.'''
keys = path.split('.')
return _find(data, keys)
To be used like this:
data = {'a': 'b', 'c': ['d', {'e': ['i', {'j': 'h'}]}]}
value = find(data, 'c.e.j')
print(value) # --> h

Change values in dict of nested dicts using items in a list?

How would you modify/create keys/values in a dict of nested dicts based on the values of a list, in which the last item of the list is a value for the dict, and the rest of items reefer to keys within dicts?
This would be the list:
list_adddress = [ "key1", "key1.2", "key1.2.1", "value" ]
This would only be a problem in situations like when parsing command line arguments. It's obvious that modifying/creating this value within a script would be pretty easy using dict_nested["key1"]["key1.2"]["key1.2.1"]["value"].
This would be a nested dict of dicts:
dict_nested = {
"key1": {
"key1.1": {
"...": "...",
},
"key1.2": {
"key1.2.1": "change_this",
},
},
"key2": {
"...": "..."
},
}
I guess that in this case, something like a recursive function or a list comprehension would be required.
def ValueModify(list_address, dict_nested):
...
...
ValueModify(..., ...)
Also, if items in list_address would reefer to keys in non-existing dictionaries, they should be created.
One-liner:
keys, (newkey, newvalue) = list_address[:-2], list_address[-2:]
reduce(dict.__getitem__, keys, dict_nested)[newkey] = newvalue
Note: dict.get and operator.getitem would produce wrong exceptions here.
An explicit for-loop as in Joel Cornett's answer might be more readable.
If you want to create non-existing intermediate dictionaries:
reduce(lambda d,k: d.setdefault(k, {}), keys, dict_nested)[newkey] = newvalue
If you want to override existing intermediate values that are not dictionaries e.g., strings, integers:
from collections import MutableMapping
def set_value(d, keys, newkey, newvalue, default_factory=dict):
"""
Equivalent to `reduce(dict.get, keys, d)[newkey] = newvalue`
if all `keys` exists and corresponding values are of correct type
"""
for key in keys:
try:
val = d[key]
except KeyError:
val = d[key] = default_factory()
else:
if not isinstance(val, MutableMapping):
val = d[key] = default_factory()
d = val
d[newkey] = newvalue
Example
list_address = ["key1", "key1.2", "key1.2.1", "key1.2.1.1", "value"]
dict_nested = {
"key1": {
"key1.1": {
"...": "...",
},
"key1.2": {
"key1.2.1": "change_this",
},
},
"key2": {
"...": "..."
},
}
set_value(dict_nested, list_address[:-2], *list_address[-2:])
assert reduce(dict.get, list_address[:-1], dict_nested) == list_address[-1]
Tests
>>> from collections import OrderedDict
>>> d = OrderedDict()
>>> set_value(d, [], 'a', 1, OrderedDict) # non-existent key
>>> d.items()
[('a', 1)]
>>> set_value(d, 'b', 'a', 2) # non-existent intermediate key
>>> d.items()
[('a', 1), ('b', {'a': 2})]
>>> set_value(d, 'a', 'b', 3) # wrong intermediate type
>>> d.items()
[('a', {'b': 3}), ('b', {'a': 2})]
>>> d = {}
>>> set_value(d, 'abc', 'd', 4)
>>> reduce(dict.get, 'abcd', d) == d['a']['b']['c']['d'] == 4
True
>>> from collections import defaultdict
>>> autovivify = lambda: defaultdict(autovivify)
>>> d = autovivify()
>>> set_value(d, 'abc', 'd', 4)
>>> reduce(dict.get, 'abcd', d) == d['a']['b']['c']['d'] == 4
True
>>> set_value(1, 'abc', 'd', 4) #doctest:+IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError:
>>> set_value([], 'abc', 'd', 4) #doctest:+IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError:
>>> L = [10]
>>> set_value(L, [0], 2, 3)
>>> L
[{2: 3}]
address_list = ["key1", "key1.1", "key1.2", "value"]
def set_value(dict_nested, address_list):
cur = dict_nested
for path_item in address_list[:-2]:
try:
cur = cur[path_item]
except KeyError:
cur = cur[path_item] = {}
cur[address_list[-2]] = address_list[-1]
I think this works like you're after.
def ValueModify(l, d):
if l[0] not in d:
d[l[0]] = dict()
if isinstance(d[l[0]], dict):
ValueModify(l[1:], d[l[0]])
else:
d[l[0]] = l[1]
I'm using isinstance, which is type checking, and not generally something you do in Python, but it does set the value as expected.
-- Edited --
Added in the missing key check to set nested values if the original nested_dict is not fully populated.
Here's a recursive solution.
def unravel(d, keys):
i = keys[0]
keys = keys[1:]
tmpDict = d[i]
if type(tmpDict) != type({}):
return tmpDict
else:
return unravel(tmpDict, keys)
To insert a new key-value pair or update the value of a pair:
import copy
def update_nested_map(d, u, *keys):
d = copy.deepcopy(d)
keys = keys[0]
if len(keys) > 1:
d[keys[0]] = update_nested_map(d[keys[0]], u, keys[1:])
else:
d[keys[0]] = u
return d
test:
>>> d = {'m': {'d': {'v': {'w': 1}}}}
>>> update_nested_map(d, 999, ['m', 'd', 'v', 'w'])
{'m': {'d': {'v': {'w': 999}}}}
>>> update_nested_map(d, 999, ['m', 'd', 'v', 'z'])
{'m': {'d': {'v': {'z': 999, 'w': 1}}}}
>>> update_nested_map(d, 999, ['m', 'd', 'l'])
{'m': {'d': {'v': {'w': 1}, 'l': 999}}}
>>> update_nested_map(d, 999, ['m','d'])
{'m': {'d': 999}}

How to convert a strictly sorted list of strings into dict?

I have a strictly sorted list of strings:
['a',
'b',
'b/c',
'b/d',
'e',
'f',
'f/g',
'f/h',
'f/h/i',
'f/h/i/j']
This list is similar to tree representation. So, I need to convert it to dict:
{'a': {},
'b': {'c': {},
'd': {}},
'e': {},
'f': {'g': {},
'h': {'i': {'j': {}}}}}
As you can see, keys in this dict are parents and values are children.
UPD: I agree that empty dict is better than None
If you don't insist on None as the leaf value, you can use the compact code
my_dict = lambda: defaultdict(my_dict)
d = my_dict()
for x in my_list:
reduce(defaultdict.__getitem__, x.split("/"), d)
Admittedly, it isn't that obvious what this code does, but it's succinct :)
di = {}
for a in arr:
al = a.split("/")
d = di
for elem in al:
if elem in d:
d = d[elem]
else:
d[elem]={}
print di
Note that elemts are not stored in alphabetical order in a dictionary!
Hope it helps, recursive approach :)
import pprint
l = ['a',
'b',
'b/c',
'b/d',
'e',
'f',
'f/g',
'f/h',
'f/h/i',
'f/h/i/j']
def put(d, elems):
f = elems[0]
if len(elems)==1:
d[f]=None
else:
if f not in d or d[f]==None:
d[f] = {}
put(d[f], elems[1:])
d = {}
for x in l:
put(d, x.split('/'))
pprint.pprint(d)
Here's my crack at it. I reverse the path for optimization, because pop() is much faster than pop(0).
def add_branch(root, path):
branch = path.pop()
if path:
if branch not in root or root[branch] is None:
root[branch] = {}
add_branch(root[branch], path)
else:
root[branch] = None
def totree(strings):
root = {}
for string in strings:
path = string.split("/")
path.reverse()
add_branch(root, path)
return root
Use like this:
my_tree = totree(['a', 'b', 'b/c', 'b/d', 'e', 'f', 'f/g', 'f/h',
'f/h/i', 'f/h/i/j'])
this is my resolution:
from collections import defaultdict
from pprint import pprint
input = ['a', 'b', 'b/c', 'b/d', 'e', 'f', 'f/g', 'f/h', 'f/h/i', 'f/h/i/j']
result = defaultdict(dict)
for i in input:
path = i.split('/')
key = path[0]
value = {}
buffer = {key:value}
for folder in path[1:]:
value[folder] = {}
value = value[folder]
result[key].update(buffer[key])
pprint(dict(result))

Check if dictionary has multiple keys

How can I check if a dictionary (actually dictionary-like object) has all of a given set of keys (plural)?
So far, I have used:
d = { 'a': 1, 'b': 2, 'c': 3 }
keys = ('a', 'b')
def has_keys(d, keys):
for key in keys:
if not key in d:
return False
return True
Is there a more elegant and Pythonic way of doing this?
Use the builtin function all()
>>> d = { 'a': 1, 'b': 2, 'c': 3 }
>>> keys = ('a', 'b')
>>> all(elem in d for elem in keys)
True
>>> keys = ('a', 'b', 'd')
>>> all(elem in d for elem in keys)
False
You may also try like this:
>>> names = {
'a' : 11,
'b' : 10,
'c' : 14,
'd': 7
}
>>> keys = ('a', 'b')
>>> set(keys).issubset(names)
True
You can just use keyword "in"
ex:
d = { 'a': 1, 'b': 2, 'c': 3 }
if 'd' in d:
print 'yes'
else:
print 'no'

Resources