8

I do have a list as given below -

keyList1 = ["Person", "Male", "Boy", "Student", "id_123", "Name"]
value1 = "Roger"

How can I generate dynamic dictionary which can be retrieved as below -

mydict["Person"]["Male"]["Boy"]["Student"]["id_123"]["Name"] = value

The list could be anything; Variable Length or consisting of "N" number of elements unknown to me...

Now I do have another list, so that My dictionary should be updated accordingly

keyList2 = ["Person", "Male", "Boy", "Student", "id_123", "Age"]
value2 = 25

i.e. If Keys "Person", "Male", "Boy", "Student", "id_123" already exists, the new key "age" should be appended ...

2
  • I would suggest to append all items in the list and use the resultant string as key. It would be lot easier. Commented Jul 4, 2013 at 4:54
  • The answers to this question may help: stackoverflow.com/questions/16384174/… Commented Jul 4, 2013 at 4:54

7 Answers 7

8

I'm just learning python, so my code could be not very pythonic, but here's my code

d = {}

keyList1 = ["Person", "Male", "Boy", "Student", "id_123", "Name"]
keyList2 = ["Person", "Male", "Boy", "Student", "id_123", "Age"]
value1 = "Roger"
value2 = 3

def insert(cur, list, value):
    if len(list) == 1:
        cur[list[0]] = value
        return
    if not cur.has_key(list[0]):
        cur[list[0]] = {}
    insert(cur[list[0]], list[1:], value)

insert(d, keyList1, value1)
insert(d, keyList2, value2)

{'Person': {'Male': {'Boy': {'Student': {'id_123': {'Age': 3, 'Name': 'Roger'}}}}}}
Sign up to request clarification or add additional context in comments.

4 Comments

This ends up being pretty similar to my solution. One simple suggestion: avoid .has_key whenever possible; prefer the quicker and more understandable if list[0] not in cur.
thanks, I agree that it's more readable and readability counts, but why it's quicker?
Try it using timeit. On my computer, in is twice as fast as .has_key (0.0672µs vs. 0.117µs). .has_key is a method lookup, whereas in is a builtin which is dispatched much quicker.
.has_key is deprecated and doesn't exist in Python3
4

You can do this by making nested defaultdicts:

from collections import defaultdict

def recursive_defaultdict():
    return defaultdict(recursive_defaultdict)

def setpath(d, p, k):
    if len(p) == 1:
        d[p[0]] = k
    else:
        setpath(d[p[0]], p[1:], k)

mydict = recursive_defaultdict()

setpath(mydict, ["Person", "Male", "Boy", "Student", "id_123", "Name"], 'Roger')

print mydict["Person"]["Male"]["Boy"]["Student"]["id_123"]["Name"]
# prints 'Roger'

This has the nice advantage of being able to write

mydict['a']['b'] = 4

without necessarily having to use the setpath helper.

You can do it without recursive defaultdicts too:

def setpath(d, p, k):
    if len(p) == 1:
        d[p[0]] = k
    else:
        setpath(d.setdefault(p[0], {}), p[1:], k)

Comments

3

Perhaps you could subclass dict:

class ChainDict(dict):
    def set_key_chain(self, keyList, value):
        t = self
        for k in keyList[:-1]:
            t = t.setdefault(k, {})
        t.setdefault(keyList[-1], value)

c = ChainDict()
c.set_key_chain(['Person', 'Male', 'Boy', 'Student', 'id_123', 'Name'], 'Roger')
print c
>>{'Person': {'Male': {'Boy': {'Student': {'id_123': {'Name': 'Roger'}}}}}}

c.set_key_chain(['Person', 'Male', 'Boy', 'Student', 'id_123', 'Age'], 25)
print c
>>{'Person': {'Male': {'Boy': {'Student': {'id_123': {'Age': 25,
      'Name': 'Roger'}}}}}}

Comments

1

Create your own class derived from dict where the init method takes a list and a single value as inputs and iterate through the list setting the keys to value, define an update method that takes a list and a new value and for each item that is not already a key set it to the new value, (assuming that is what you need).

Forget the idea of

mydict["Person"]["Male"]["Boy"]["Student"]["id_123"]["Name"] = value1`

as it is confusing with subindexes.

Comments

0

I am trying to dealing with similar stuff, so I can suggest some guidelines, but again I am naive in Python, so this is just a guideline...

you have a list of keys , so you can definitely start with a loop iterating for each value and then assign the value

like

for i in keylist:
if type(keylist[i]) == dict:
        do something
    else:
        keylist[i] = {}

in the do something, you need to increment i and change index to [i][i+1] and then follow the same untill i+n = len(keylist)

Comments

0

Use tuple(keyList1) as key. (tuples are immutable and therefore can be dict keys).

You will have a world of headache with the nested dict approach. (nested loops for enumeration, legacy data when the hierarchy needs to change, etc.).

On a second thought, maybe you should define a person class

class Person(object):
    gender = "Male"
    group = "Student"
    id = 123
    Name = "John Doe"

then use a list of all persons and filter with e.g.

male_students = [s for s in  ALL_PERSONS where s.gender=="Male" and s.group="Student"]

... for <= 10000 students you should be fine performancewise.

Comments

-2
>>> mydict = {}
>>> keyList1 = ["Person", "Male", "Boy", "Student", "id_123", "Name"]
>>> value1 = "Roger"
>>> reduce(lambda x, y: x.setdefault(y, {}), keyList1, mydict)
{}
>>> mydict["Person"]["Male"]["Boy"]["Student"]["id_123"]["Name"] = value1

You can also do it in one step like this

>>> keyList2 = ["Person", "Male", "Boy", "Student", "id_123", "Age"]
>>> value2 = 25
>>> reduce(lambda x,y: x.setdefault(y,{}), keyList2[:-1], mydict).update({keyList2[-1]: value2})

2 Comments

>>> mydict {'Person': {'Male': {'Boy': {'Student': {'id_123': {'Name': 'Roger'}}}}}}
@AbhishekKulkarni, er.. where does reduce(lambda x, y: x.setdefault(y, {}), keyList1, mydict) depend on the number of elements or the specific keys?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.