Access nested dictionary items via a list of keys?












102















I have a complex dictionary structure which I would like to access via a list of keys to address the correct item.



dataDict = {
"a":{
"r": 1,
"s": 2,
"t": 3
},
"b":{
"u": 1,
"v": {
"x": 1,
"y": 2,
"z": 3
},
"w": 3
}
}

maplist = ["a", "r"]


or



maplist = ["b", "v", "y"]


I have made the following code which works but I'm sure there is a better and more efficient way to do this if anyone has an idea.



# Get a given data from a dictionary with position provided as a list
def getFromDict(dataDict, mapList):
for k in mapList: dataDict = dataDict[k]
return dataDict

# Set a given data in a dictionary with position provided as a list
def setInDict(dataDict, mapList, value):
for k in mapList[:-1]: dataDict = dataDict[k]
dataDict[mapList[-1]] = value









share|improve this question

























  • See also: stackoverflow.com/questions/7681301/… stackoverflow.com/a/16508328/42223

    – dreftymac
    Oct 30 '17 at 19:55
















102















I have a complex dictionary structure which I would like to access via a list of keys to address the correct item.



dataDict = {
"a":{
"r": 1,
"s": 2,
"t": 3
},
"b":{
"u": 1,
"v": {
"x": 1,
"y": 2,
"z": 3
},
"w": 3
}
}

maplist = ["a", "r"]


or



maplist = ["b", "v", "y"]


I have made the following code which works but I'm sure there is a better and more efficient way to do this if anyone has an idea.



# Get a given data from a dictionary with position provided as a list
def getFromDict(dataDict, mapList):
for k in mapList: dataDict = dataDict[k]
return dataDict

# Set a given data in a dictionary with position provided as a list
def setInDict(dataDict, mapList, value):
for k in mapList[:-1]: dataDict = dataDict[k]
dataDict[mapList[-1]] = value









share|improve this question

























  • See also: stackoverflow.com/questions/7681301/… stackoverflow.com/a/16508328/42223

    – dreftymac
    Oct 30 '17 at 19:55














102












102








102


47






I have a complex dictionary structure which I would like to access via a list of keys to address the correct item.



dataDict = {
"a":{
"r": 1,
"s": 2,
"t": 3
},
"b":{
"u": 1,
"v": {
"x": 1,
"y": 2,
"z": 3
},
"w": 3
}
}

maplist = ["a", "r"]


or



maplist = ["b", "v", "y"]


I have made the following code which works but I'm sure there is a better and more efficient way to do this if anyone has an idea.



# Get a given data from a dictionary with position provided as a list
def getFromDict(dataDict, mapList):
for k in mapList: dataDict = dataDict[k]
return dataDict

# Set a given data in a dictionary with position provided as a list
def setInDict(dataDict, mapList, value):
for k in mapList[:-1]: dataDict = dataDict[k]
dataDict[mapList[-1]] = value









share|improve this question
















I have a complex dictionary structure which I would like to access via a list of keys to address the correct item.



dataDict = {
"a":{
"r": 1,
"s": 2,
"t": 3
},
"b":{
"u": 1,
"v": {
"x": 1,
"y": 2,
"z": 3
},
"w": 3
}
}

maplist = ["a", "r"]


or



maplist = ["b", "v", "y"]


I have made the following code which works but I'm sure there is a better and more efficient way to do this if anyone has an idea.



# Get a given data from a dictionary with position provided as a list
def getFromDict(dataDict, mapList):
for k in mapList: dataDict = dataDict[k]
return dataDict

# Set a given data in a dictionary with position provided as a list
def setInDict(dataDict, mapList, value):
for k in mapList[:-1]: dataDict = dataDict[k]
dataDict[mapList[-1]] = value






python list dictionary






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 29 '18 at 3:04









wim

159k50304436




159k50304436










asked Feb 4 '13 at 18:04









kolergykolergy

636266




636266













  • See also: stackoverflow.com/questions/7681301/… stackoverflow.com/a/16508328/42223

    – dreftymac
    Oct 30 '17 at 19:55



















  • See also: stackoverflow.com/questions/7681301/… stackoverflow.com/a/16508328/42223

    – dreftymac
    Oct 30 '17 at 19:55

















See also: stackoverflow.com/questions/7681301/… stackoverflow.com/a/16508328/42223

– dreftymac
Oct 30 '17 at 19:55





See also: stackoverflow.com/questions/7681301/… stackoverflow.com/a/16508328/42223

– dreftymac
Oct 30 '17 at 19:55












14 Answers
14






active

oldest

votes


















163














Use reduce() to traverse the dictionary:



from functools import reduce  # forward compatibility for Python 3
import operator

def getFromDict(dataDict, mapList):
return reduce(operator.getitem, mapList, dataDict)


and reuse getFromDict to find the location to store the value for setInDict():



def setInDict(dataDict, mapList, value):
getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value


All but the last element in mapList is needed to find the 'parent' dictionary to add the value to, then use the last element to set the value to the right key.



Demo:



>>> getFromDict(dataDict, ["a", "r"])
1
>>> getFromDict(dataDict, ["b", "v", "y"])
2
>>> setInDict(dataDict, ["b", "v", "w"], 4)
>>> import pprint
>>> pprint.pprint(dataDict)
{'a': {'r': 1, 's': 2, 't': 3},
'b': {'u': 1, 'v': {'w': 4, 'x': 1, 'y': 2, 'z': 3}, 'w': 3}}


Note that the Python PEP8 style guide prescribes snake_case names for functions. The above works equally well for lists or a mix of dictionaries and lists, so the names should really be get_by_path() and set_by_path():



from functools import reduce  # forward compatibility for Python 3
import operator

def get_by_path(root, items):
"""Access a nested object in root by item sequence."""
return reduce(operator.getitem, items, root)

def set_by_path(root, items, value):
"""Set a value in a nested object in root by item sequence."""
get_by_path(root, items[:-1])[items[-1]] = value





share|improve this answer


























  • How much such traversing is reliable for arbitrary nested structures? Will it work for mixed dictionaries with nested lists as well? How do I modify getFromDict() to supply default_value and to have default default_value as None? I am novice in Python with many years of PHP development and before C development.

    – Dmitriy Sintsov
    Feb 11 '15 at 10:51








  • 1





    Also nested mapped set should create non-existing nodes, imo: lists for integer keys, dictionaries for string keys.

    – Dmitriy Sintsov
    Feb 11 '15 at 10:59








  • 1





    @user1353510: as it happens, regular indexing syntax is used here, so it'll support lists inside dictionaries too. Just pass in integer indexes for those.

    – Martijn Pieters
    Feb 11 '15 at 12:04











  • @user1353510: different usecases call for different behaviour. The code here doesn't create intermediaries, no.

    – Martijn Pieters
    Feb 11 '15 at 12:04






  • 1





    @user1353510: for a default value, use try:, except (KeyError, IndexError): return default_value around the current return line.

    – Martijn Pieters
    Feb 11 '15 at 12:05



















27















  1. The accepted solution won't work directly for python3 - it will need an from functools import reduce.

  2. Also it seems more pythonic to use a for loop.
    See the quote from What’s New In Python 3.0.


    Removed reduce(). Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable.




  3. Next, the accepted solution doesn't set non-existing nested keys (it returns a KeyError) - see @eafit's answer for a solution


So why not use the suggested method from kolergy's question for getting a value:



def getFromDict(dataDict, mapList):    
for k in mapList: dataDict = dataDict[k]
return dataDict


And the code from @eafit's answer for setting a value:



def nested_set(dic, keys, value):
for key in keys[:-1]:
dic = dic.setdefault(key, {})
dic[keys[-1]] = value


Both work straight in python 2 and 3






share|improve this answer





















  • 1





    I prefer this solution - but be careful. If I'm not mistaken, since Python dictionaries are not immutable getFromDict has the potential to destroy the caller's dataDict. I would copy.deepcopy(dataDict) first. Of course, (as written) this behavior is desired in the second function.

    – Dylan F
    Jun 13 '18 at 3:33



















12














Using reduce is clever, but the OP's set method may have issues if the parent keys do not pre-exist in the nested dictionary. Since this is the first SO post I saw for this subject in my google search, I would like to make it slightly better.



The set method in ( Setting a value in a nested python dictionary given a list of indices and value ) seems more robust to missing parental keys. To copy it over:



def nested_set(dic, keys, value):
for key in keys[:-1]:
dic = dic.setdefault(key, {})
dic[keys[-1]] = value


Also, it can be convenient to have a method that traverses the key tree and get all the absolute key paths, for which I have created:



def keysInDict(dataDict, parent=):
if not isinstance(dataDict, dict):
return [tuple(parent)]
else:
return reduce(list.__add__,
[keysInDict(v,parent+[k]) for k,v in dataDict.items()], )


One use of it is to convert the nested tree to a pandas DataFrame, using the following code (assuming that all leafs in the nested dictionary have the same depth).



def dict_to_df(dataDict):
ret =
for k in keysInDict(dataDict):
v = np.array( getFromDict(dataDict, k), )
v = pd.DataFrame(v)
v.columns = pd.MultiIndex.from_product(list(k) + [v.columns])
ret.append(v)
return reduce(pd.DataFrame.join, ret)





share|improve this answer

































    8














    This library may be helpful: https://github.com/akesterson/dpath-python




    A python library for accessing and searching dictionaries via
    /slashed/paths ala xpath



    Basically it lets you glob over a dictionary as if it were a
    filesystem.







    share|improve this answer

































      1














      Instead of taking a performance hit each time you want to look up a value, how about you flatten the dictionary once then simply look up the key like b:v:y



      def flatten(mydict):
      new_dict = {}
      for key,value in mydict.items():
      if type(value) == dict:
      _dict = {':'.join([key, _key]):_value for _key, _value in flatten(value).items()}
      new_dict.update(_dict)
      else:
      new_dict[key]=value
      return new_dict

      dataDict = {
      "a":{
      "r": 1,
      "s": 2,
      "t": 3
      },
      "b":{
      "u": 1,
      "v": {
      "x": 1,
      "y": 2,
      "z": 3
      },
      "w": 3
      }
      }

      flat_dict = flatten(dataDict)
      print flat_dict
      {'b:w': 3, 'b:u': 1, 'b:v:y': 2, 'b:v:x': 1, 'b:v:z': 3, 'a:r': 1, 'a:s': 2, 'a:t': 3}


      This way you can simply look up items using flat_dict['b:v:y'] which will give you 1.



      And instead of traversing the dictionary on each lookup, you may be able to speed this up by flattening the dictionary and saving the output so that a lookup from cold start would mean loading up the flattened dictionary and simply performing a key/value lookup with no traversal.






      share|improve this answer































        1














        How about using recursive functions?



        To get a value:



        def getFromDict(dataDict, maplist):
        first, rest = maplist[0], maplist[1:]

        if rest:
        # if `rest` is not empty, run the function recursively
        return getFromDict(dataDict[first], rest)
        else:
        return dataDict[first]


        And to set a value:



        def setInDict(dataDict, maplist, value):
        first, rest = maplist[0], maplist[1:]

        if rest:
        try:
        if not isinstance(dataDict[first], dict):
        # if the key is not a dict, then make it a dict
        dataDict[first] = {}
        except KeyError:
        # if key doesn't exist, create one
        dataDict[first] = {}

        setInDict(dataDict[first], rest, value)
        else:
        dataDict[first] = value





        share|improve this answer





















        • 1





          Great solution!

          – josifoski
          Jun 25 '18 at 14:21



















        1














        Pure Python style, without any import:



        def nested_set(element, value, *keys):
        if type(element) is not dict:
        raise AttributeError('nested_set() expects dict as first argument.')
        if len(keys) < 2:
        raise AttributeError('nested_set() expects at least three arguments, not enough given.')

        _keys = keys[:-1]
        _element = element
        for key in _keys:
        _element = _element[key]
        _element[keys[-1]] = value

        example = {"foo": { "bar": { "baz": "ok" } } }
        keys = ['foo', 'bar']
        nested_set(example, "yay", *keys)
        print(example)


        Output



        {'foo': {'bar': 'yay'}}





        share|improve this answer































          1














          An alternative way if you don't want to raise errors if one of the keys is absent (so that your main code can run without interruption):



          def get_value(self,your_dict,*keys):
          curr_dict_ = your_dict
          for k in keys:
          v = curr_dict.get(k,None)
          if v is None:
          break
          if isinstance(v,dict):
          curr_dict = v
          return v


          In this case, if any of the input keys is not present, None is returned, which can be used as a check in your main code to perform an alternative task.






          share|improve this answer































            1














            How about check and then set dict element without processing all indexes twice?



            Solution:



            def nested_yield(nested, keys_list):
            """
            Get current nested data by send(None) method. Allows change it to Value by calling send(Value) next time
            :param nested: list or dict of lists or dicts
            :param keys_list: list of indexes/keys
            """
            if not len(keys_list): # assign to 1st level list
            if isinstance(nested, list):
            while True:
            nested[:] = yield nested
            else:
            raise IndexError('Only lists can take element without key')


            last_key = keys_list.pop()
            for key in keys_list:
            nested = nested[key]

            while True:
            try:
            nested[last_key] = yield nested[last_key]
            except IndexError as e:
            print('no index {} in {}'.format(last_key, nested))
            yield None


            Example workflow:



            ny = nested_yield(nested_dict, nested_address)
            data_element = ny.send(None)
            if data_element:
            # process element
            ...
            else:
            # extend/update nested data
            ny.send(new_data_element)
            ...
            ny.close()


            Test



            >>> cfg= {'Options': [[1,[0]],[2,[4,[8,16]]],[3,[9]]]}
            ny = nested_yield(cfg, ['Options',1,1,1])
            ny.send(None)
            [8, 16]
            >>> ny.send('Hello!')
            'Hello!'
            >>> cfg
            {'Options': [[1, [0]], [2, [4, 'Hello!']], [3, [9]]]}
            >>> ny.close()





            share|improve this answer

































              0














              Solved this with recursion:



              def get(d,l):
              if len(l)==1: return d[l[0]]
              return get(d[l[0]],l[1:])


              Using your example:



              dataDict = {
              "a":{
              "r": 1,
              "s": 2,
              "t": 3
              },
              "b":{
              "u": 1,
              "v": {
              "x": 1,
              "y": 2,
              "z": 3
              },
              "w": 3
              }
              }
              maplist1 = ["a", "r"]
              maplist2 = ["b", "v", "y"]
              print(get(dataDict, maplist1)) # 1
              print(get(dataDict, maplist2)) # 2





              share|improve this answer































                0














                If you also want the ability to work with arbitrary json including nested lists and dicts, and nicely handle invalid lookup paths, here's my solution:



                from functools import reduce


                def get_furthest(s, path):
                '''
                Gets the furthest value along a given key path in a subscriptable structure.

                subscriptable, list -> any
                :param s: the subscriptable structure to examine
                :param path: the lookup path to follow
                :return: a tuple of the value at the furthest valid key, and whether the full path is valid
                '''

                def step_key(acc, key):
                s = acc[0]
                if isinstance(s, str):
                return (s, False)
                try:
                return (s[key], acc[1])
                except LookupError:
                return (s, False)

                return reduce(step_key, path, (s, True))


                def get_val(s, path):
                val, successful = get_furthest(s, path)
                if successful:
                return val
                else:
                raise LookupError('Invalid lookup path: {}'.format(path))


                def set_val(s, path, value):
                get_val(s, path[:-1])[path[-1]] = value





                share|improve this answer































                  0














                  It's satisfying to see these answers for having two static methods for setting & getting nested attributes. These solutions are way better than using nested trees https://gist.github.com/hrldcpr/2012250



                  Here's my implementation.



                  Usage:



                  To set nested attribute call sattr(my_dict, 1, 2, 3, 5) is equal to my_dict[1][2][3][4]=5



                  To get a nested attribute call gattr(my_dict, 1, 2)



                  def gattr(d, *attrs):
                  """
                  This method receives a dict and list of attributes to return the innermost value of the give dict
                  """
                  try:
                  for at in attrs:
                  d = d[at]
                  return d
                  except:
                  return None


                  def sattr(d, *attrs):
                  """
                  Adds "val" to dict in the hierarchy mentioned via *attrs
                  For ex:
                  sattr(animals, "cat", "leg","fingers", 4) is equivalent to animals["cat"]["leg"]["fingers"]=4
                  This method creates necessary objects until it reaches the final depth
                  This behaviour is also known as autovivification and plenty of implementation are around
                  This implementation addresses the corner case of replacing existing primitives
                  https://gist.github.com/hrldcpr/2012250#gistcomment-1779319
                  """
                  for attr in attrs[:-2]:
                  # If such key is not found or the value is primitive supply an empty dict
                  if d.get(attr) is None or isinstance(d.get(attr), dict):
                  d[attr] = {}
                  d = d[attr]
                  d[attrs[-2]] = attrs[-1]





                  share|improve this answer































                    0














                    Very late to the party, but posting in case this may help someone in the future. For my use case, the following function worked the best. Works to pull any data type out of dictionary



                    dict is the dictionary containing our value



                    list is a list of "steps" towards our value



                    def getnestedvalue(dict, list):

                    length = len(list)
                    try:
                    for depth, key in enumerate(list):
                    if depth == length - 1:
                    output = dict[key]
                    return output
                    dict = dict[key]
                    except (KeyError, TypeError):
                    return None

                    return None





                    share|improve this answer































                      0














                      a method for concatenating strings:



                      def get_sub_object_from_path(dict_name, map_list):
                      for i in map_list:
                      _string = "['%s']" % i
                      dict_name += _string
                      value = eval(dict_name)
                      return value
                      #Sample:
                      _dict = {'new': 'person', 'time': {'for': 'one'}}
                      map_list = ['time', 'for']
                      print get_sub_object_from_path("_dict",map_list)
                      #Output:
                      #one





                      share|improve this answer























                        Your Answer






                        StackExchange.ifUsing("editor", function () {
                        StackExchange.using("externalEditor", function () {
                        StackExchange.using("snippets", function () {
                        StackExchange.snippets.init();
                        });
                        });
                        }, "code-snippets");

                        StackExchange.ready(function() {
                        var channelOptions = {
                        tags: "".split(" "),
                        id: "1"
                        };
                        initTagRenderer("".split(" "), "".split(" "), channelOptions);

                        StackExchange.using("externalEditor", function() {
                        // Have to fire editor after snippets, if snippets enabled
                        if (StackExchange.settings.snippets.snippetsEnabled) {
                        StackExchange.using("snippets", function() {
                        createEditor();
                        });
                        }
                        else {
                        createEditor();
                        }
                        });

                        function createEditor() {
                        StackExchange.prepareEditor({
                        heartbeatType: 'answer',
                        autoActivateHeartbeat: false,
                        convertImagesToLinks: true,
                        noModals: true,
                        showLowRepImageUploadWarning: true,
                        reputationToPostImages: 10,
                        bindNavPrevention: true,
                        postfix: "",
                        imageUploader: {
                        brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                        contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                        allowUrls: true
                        },
                        onDemand: true,
                        discardSelector: ".discard-answer"
                        ,immediatelyShowMarkdownHelp:true
                        });


                        }
                        });














                        draft saved

                        draft discarded


















                        StackExchange.ready(
                        function () {
                        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f14692690%2faccess-nested-dictionary-items-via-a-list-of-keys%23new-answer', 'question_page');
                        }
                        );

                        Post as a guest















                        Required, but never shown

























                        14 Answers
                        14






                        active

                        oldest

                        votes








                        14 Answers
                        14






                        active

                        oldest

                        votes









                        active

                        oldest

                        votes






                        active

                        oldest

                        votes









                        163














                        Use reduce() to traverse the dictionary:



                        from functools import reduce  # forward compatibility for Python 3
                        import operator

                        def getFromDict(dataDict, mapList):
                        return reduce(operator.getitem, mapList, dataDict)


                        and reuse getFromDict to find the location to store the value for setInDict():



                        def setInDict(dataDict, mapList, value):
                        getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value


                        All but the last element in mapList is needed to find the 'parent' dictionary to add the value to, then use the last element to set the value to the right key.



                        Demo:



                        >>> getFromDict(dataDict, ["a", "r"])
                        1
                        >>> getFromDict(dataDict, ["b", "v", "y"])
                        2
                        >>> setInDict(dataDict, ["b", "v", "w"], 4)
                        >>> import pprint
                        >>> pprint.pprint(dataDict)
                        {'a': {'r': 1, 's': 2, 't': 3},
                        'b': {'u': 1, 'v': {'w': 4, 'x': 1, 'y': 2, 'z': 3}, 'w': 3}}


                        Note that the Python PEP8 style guide prescribes snake_case names for functions. The above works equally well for lists or a mix of dictionaries and lists, so the names should really be get_by_path() and set_by_path():



                        from functools import reduce  # forward compatibility for Python 3
                        import operator

                        def get_by_path(root, items):
                        """Access a nested object in root by item sequence."""
                        return reduce(operator.getitem, items, root)

                        def set_by_path(root, items, value):
                        """Set a value in a nested object in root by item sequence."""
                        get_by_path(root, items[:-1])[items[-1]] = value





                        share|improve this answer


























                        • How much such traversing is reliable for arbitrary nested structures? Will it work for mixed dictionaries with nested lists as well? How do I modify getFromDict() to supply default_value and to have default default_value as None? I am novice in Python with many years of PHP development and before C development.

                          – Dmitriy Sintsov
                          Feb 11 '15 at 10:51








                        • 1





                          Also nested mapped set should create non-existing nodes, imo: lists for integer keys, dictionaries for string keys.

                          – Dmitriy Sintsov
                          Feb 11 '15 at 10:59








                        • 1





                          @user1353510: as it happens, regular indexing syntax is used here, so it'll support lists inside dictionaries too. Just pass in integer indexes for those.

                          – Martijn Pieters
                          Feb 11 '15 at 12:04











                        • @user1353510: different usecases call for different behaviour. The code here doesn't create intermediaries, no.

                          – Martijn Pieters
                          Feb 11 '15 at 12:04






                        • 1





                          @user1353510: for a default value, use try:, except (KeyError, IndexError): return default_value around the current return line.

                          – Martijn Pieters
                          Feb 11 '15 at 12:05
















                        163














                        Use reduce() to traverse the dictionary:



                        from functools import reduce  # forward compatibility for Python 3
                        import operator

                        def getFromDict(dataDict, mapList):
                        return reduce(operator.getitem, mapList, dataDict)


                        and reuse getFromDict to find the location to store the value for setInDict():



                        def setInDict(dataDict, mapList, value):
                        getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value


                        All but the last element in mapList is needed to find the 'parent' dictionary to add the value to, then use the last element to set the value to the right key.



                        Demo:



                        >>> getFromDict(dataDict, ["a", "r"])
                        1
                        >>> getFromDict(dataDict, ["b", "v", "y"])
                        2
                        >>> setInDict(dataDict, ["b", "v", "w"], 4)
                        >>> import pprint
                        >>> pprint.pprint(dataDict)
                        {'a': {'r': 1, 's': 2, 't': 3},
                        'b': {'u': 1, 'v': {'w': 4, 'x': 1, 'y': 2, 'z': 3}, 'w': 3}}


                        Note that the Python PEP8 style guide prescribes snake_case names for functions. The above works equally well for lists or a mix of dictionaries and lists, so the names should really be get_by_path() and set_by_path():



                        from functools import reduce  # forward compatibility for Python 3
                        import operator

                        def get_by_path(root, items):
                        """Access a nested object in root by item sequence."""
                        return reduce(operator.getitem, items, root)

                        def set_by_path(root, items, value):
                        """Set a value in a nested object in root by item sequence."""
                        get_by_path(root, items[:-1])[items[-1]] = value





                        share|improve this answer


























                        • How much such traversing is reliable for arbitrary nested structures? Will it work for mixed dictionaries with nested lists as well? How do I modify getFromDict() to supply default_value and to have default default_value as None? I am novice in Python with many years of PHP development and before C development.

                          – Dmitriy Sintsov
                          Feb 11 '15 at 10:51








                        • 1





                          Also nested mapped set should create non-existing nodes, imo: lists for integer keys, dictionaries for string keys.

                          – Dmitriy Sintsov
                          Feb 11 '15 at 10:59








                        • 1





                          @user1353510: as it happens, regular indexing syntax is used here, so it'll support lists inside dictionaries too. Just pass in integer indexes for those.

                          – Martijn Pieters
                          Feb 11 '15 at 12:04











                        • @user1353510: different usecases call for different behaviour. The code here doesn't create intermediaries, no.

                          – Martijn Pieters
                          Feb 11 '15 at 12:04






                        • 1





                          @user1353510: for a default value, use try:, except (KeyError, IndexError): return default_value around the current return line.

                          – Martijn Pieters
                          Feb 11 '15 at 12:05














                        163












                        163








                        163







                        Use reduce() to traverse the dictionary:



                        from functools import reduce  # forward compatibility for Python 3
                        import operator

                        def getFromDict(dataDict, mapList):
                        return reduce(operator.getitem, mapList, dataDict)


                        and reuse getFromDict to find the location to store the value for setInDict():



                        def setInDict(dataDict, mapList, value):
                        getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value


                        All but the last element in mapList is needed to find the 'parent' dictionary to add the value to, then use the last element to set the value to the right key.



                        Demo:



                        >>> getFromDict(dataDict, ["a", "r"])
                        1
                        >>> getFromDict(dataDict, ["b", "v", "y"])
                        2
                        >>> setInDict(dataDict, ["b", "v", "w"], 4)
                        >>> import pprint
                        >>> pprint.pprint(dataDict)
                        {'a': {'r': 1, 's': 2, 't': 3},
                        'b': {'u': 1, 'v': {'w': 4, 'x': 1, 'y': 2, 'z': 3}, 'w': 3}}


                        Note that the Python PEP8 style guide prescribes snake_case names for functions. The above works equally well for lists or a mix of dictionaries and lists, so the names should really be get_by_path() and set_by_path():



                        from functools import reduce  # forward compatibility for Python 3
                        import operator

                        def get_by_path(root, items):
                        """Access a nested object in root by item sequence."""
                        return reduce(operator.getitem, items, root)

                        def set_by_path(root, items, value):
                        """Set a value in a nested object in root by item sequence."""
                        get_by_path(root, items[:-1])[items[-1]] = value





                        share|improve this answer















                        Use reduce() to traverse the dictionary:



                        from functools import reduce  # forward compatibility for Python 3
                        import operator

                        def getFromDict(dataDict, mapList):
                        return reduce(operator.getitem, mapList, dataDict)


                        and reuse getFromDict to find the location to store the value for setInDict():



                        def setInDict(dataDict, mapList, value):
                        getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value


                        All but the last element in mapList is needed to find the 'parent' dictionary to add the value to, then use the last element to set the value to the right key.



                        Demo:



                        >>> getFromDict(dataDict, ["a", "r"])
                        1
                        >>> getFromDict(dataDict, ["b", "v", "y"])
                        2
                        >>> setInDict(dataDict, ["b", "v", "w"], 4)
                        >>> import pprint
                        >>> pprint.pprint(dataDict)
                        {'a': {'r': 1, 's': 2, 't': 3},
                        'b': {'u': 1, 'v': {'w': 4, 'x': 1, 'y': 2, 'z': 3}, 'w': 3}}


                        Note that the Python PEP8 style guide prescribes snake_case names for functions. The above works equally well for lists or a mix of dictionaries and lists, so the names should really be get_by_path() and set_by_path():



                        from functools import reduce  # forward compatibility for Python 3
                        import operator

                        def get_by_path(root, items):
                        """Access a nested object in root by item sequence."""
                        return reduce(operator.getitem, items, root)

                        def set_by_path(root, items, value):
                        """Set a value in a nested object in root by item sequence."""
                        get_by_path(root, items[:-1])[items[-1]] = value






                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited Jul 29 '18 at 21:12

























                        answered Feb 4 '13 at 18:07









                        Martijn PietersMartijn Pieters

                        704k13324522279




                        704k13324522279













                        • How much such traversing is reliable for arbitrary nested structures? Will it work for mixed dictionaries with nested lists as well? How do I modify getFromDict() to supply default_value and to have default default_value as None? I am novice in Python with many years of PHP development and before C development.

                          – Dmitriy Sintsov
                          Feb 11 '15 at 10:51








                        • 1





                          Also nested mapped set should create non-existing nodes, imo: lists for integer keys, dictionaries for string keys.

                          – Dmitriy Sintsov
                          Feb 11 '15 at 10:59








                        • 1





                          @user1353510: as it happens, regular indexing syntax is used here, so it'll support lists inside dictionaries too. Just pass in integer indexes for those.

                          – Martijn Pieters
                          Feb 11 '15 at 12:04











                        • @user1353510: different usecases call for different behaviour. The code here doesn't create intermediaries, no.

                          – Martijn Pieters
                          Feb 11 '15 at 12:04






                        • 1





                          @user1353510: for a default value, use try:, except (KeyError, IndexError): return default_value around the current return line.

                          – Martijn Pieters
                          Feb 11 '15 at 12:05



















                        • How much such traversing is reliable for arbitrary nested structures? Will it work for mixed dictionaries with nested lists as well? How do I modify getFromDict() to supply default_value and to have default default_value as None? I am novice in Python with many years of PHP development and before C development.

                          – Dmitriy Sintsov
                          Feb 11 '15 at 10:51








                        • 1





                          Also nested mapped set should create non-existing nodes, imo: lists for integer keys, dictionaries for string keys.

                          – Dmitriy Sintsov
                          Feb 11 '15 at 10:59








                        • 1





                          @user1353510: as it happens, regular indexing syntax is used here, so it'll support lists inside dictionaries too. Just pass in integer indexes for those.

                          – Martijn Pieters
                          Feb 11 '15 at 12:04











                        • @user1353510: different usecases call for different behaviour. The code here doesn't create intermediaries, no.

                          – Martijn Pieters
                          Feb 11 '15 at 12:04






                        • 1





                          @user1353510: for a default value, use try:, except (KeyError, IndexError): return default_value around the current return line.

                          – Martijn Pieters
                          Feb 11 '15 at 12:05

















                        How much such traversing is reliable for arbitrary nested structures? Will it work for mixed dictionaries with nested lists as well? How do I modify getFromDict() to supply default_value and to have default default_value as None? I am novice in Python with many years of PHP development and before C development.

                        – Dmitriy Sintsov
                        Feb 11 '15 at 10:51







                        How much such traversing is reliable for arbitrary nested structures? Will it work for mixed dictionaries with nested lists as well? How do I modify getFromDict() to supply default_value and to have default default_value as None? I am novice in Python with many years of PHP development and before C development.

                        – Dmitriy Sintsov
                        Feb 11 '15 at 10:51






                        1




                        1





                        Also nested mapped set should create non-existing nodes, imo: lists for integer keys, dictionaries for string keys.

                        – Dmitriy Sintsov
                        Feb 11 '15 at 10:59







                        Also nested mapped set should create non-existing nodes, imo: lists for integer keys, dictionaries for string keys.

                        – Dmitriy Sintsov
                        Feb 11 '15 at 10:59






                        1




                        1





                        @user1353510: as it happens, regular indexing syntax is used here, so it'll support lists inside dictionaries too. Just pass in integer indexes for those.

                        – Martijn Pieters
                        Feb 11 '15 at 12:04





                        @user1353510: as it happens, regular indexing syntax is used here, so it'll support lists inside dictionaries too. Just pass in integer indexes for those.

                        – Martijn Pieters
                        Feb 11 '15 at 12:04













                        @user1353510: different usecases call for different behaviour. The code here doesn't create intermediaries, no.

                        – Martijn Pieters
                        Feb 11 '15 at 12:04





                        @user1353510: different usecases call for different behaviour. The code here doesn't create intermediaries, no.

                        – Martijn Pieters
                        Feb 11 '15 at 12:04




                        1




                        1





                        @user1353510: for a default value, use try:, except (KeyError, IndexError): return default_value around the current return line.

                        – Martijn Pieters
                        Feb 11 '15 at 12:05





                        @user1353510: for a default value, use try:, except (KeyError, IndexError): return default_value around the current return line.

                        – Martijn Pieters
                        Feb 11 '15 at 12:05













                        27















                        1. The accepted solution won't work directly for python3 - it will need an from functools import reduce.

                        2. Also it seems more pythonic to use a for loop.
                          See the quote from What’s New In Python 3.0.


                          Removed reduce(). Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable.




                        3. Next, the accepted solution doesn't set non-existing nested keys (it returns a KeyError) - see @eafit's answer for a solution


                        So why not use the suggested method from kolergy's question for getting a value:



                        def getFromDict(dataDict, mapList):    
                        for k in mapList: dataDict = dataDict[k]
                        return dataDict


                        And the code from @eafit's answer for setting a value:



                        def nested_set(dic, keys, value):
                        for key in keys[:-1]:
                        dic = dic.setdefault(key, {})
                        dic[keys[-1]] = value


                        Both work straight in python 2 and 3






                        share|improve this answer





















                        • 1





                          I prefer this solution - but be careful. If I'm not mistaken, since Python dictionaries are not immutable getFromDict has the potential to destroy the caller's dataDict. I would copy.deepcopy(dataDict) first. Of course, (as written) this behavior is desired in the second function.

                          – Dylan F
                          Jun 13 '18 at 3:33
















                        27















                        1. The accepted solution won't work directly for python3 - it will need an from functools import reduce.

                        2. Also it seems more pythonic to use a for loop.
                          See the quote from What’s New In Python 3.0.


                          Removed reduce(). Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable.




                        3. Next, the accepted solution doesn't set non-existing nested keys (it returns a KeyError) - see @eafit's answer for a solution


                        So why not use the suggested method from kolergy's question for getting a value:



                        def getFromDict(dataDict, mapList):    
                        for k in mapList: dataDict = dataDict[k]
                        return dataDict


                        And the code from @eafit's answer for setting a value:



                        def nested_set(dic, keys, value):
                        for key in keys[:-1]:
                        dic = dic.setdefault(key, {})
                        dic[keys[-1]] = value


                        Both work straight in python 2 and 3






                        share|improve this answer





















                        • 1





                          I prefer this solution - but be careful. If I'm not mistaken, since Python dictionaries are not immutable getFromDict has the potential to destroy the caller's dataDict. I would copy.deepcopy(dataDict) first. Of course, (as written) this behavior is desired in the second function.

                          – Dylan F
                          Jun 13 '18 at 3:33














                        27












                        27








                        27








                        1. The accepted solution won't work directly for python3 - it will need an from functools import reduce.

                        2. Also it seems more pythonic to use a for loop.
                          See the quote from What’s New In Python 3.0.


                          Removed reduce(). Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable.




                        3. Next, the accepted solution doesn't set non-existing nested keys (it returns a KeyError) - see @eafit's answer for a solution


                        So why not use the suggested method from kolergy's question for getting a value:



                        def getFromDict(dataDict, mapList):    
                        for k in mapList: dataDict = dataDict[k]
                        return dataDict


                        And the code from @eafit's answer for setting a value:



                        def nested_set(dic, keys, value):
                        for key in keys[:-1]:
                        dic = dic.setdefault(key, {})
                        dic[keys[-1]] = value


                        Both work straight in python 2 and 3






                        share|improve this answer
















                        1. The accepted solution won't work directly for python3 - it will need an from functools import reduce.

                        2. Also it seems more pythonic to use a for loop.
                          See the quote from What’s New In Python 3.0.


                          Removed reduce(). Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable.




                        3. Next, the accepted solution doesn't set non-existing nested keys (it returns a KeyError) - see @eafit's answer for a solution


                        So why not use the suggested method from kolergy's question for getting a value:



                        def getFromDict(dataDict, mapList):    
                        for k in mapList: dataDict = dataDict[k]
                        return dataDict


                        And the code from @eafit's answer for setting a value:



                        def nested_set(dic, keys, value):
                        for key in keys[:-1]:
                        dic = dic.setdefault(key, {})
                        dic[keys[-1]] = value


                        Both work straight in python 2 and 3







                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited Mar 18 '18 at 15:43

























                        answered Jun 8 '16 at 13:47









                        DomTomCatDomTomCat

                        3,96712745




                        3,96712745








                        • 1





                          I prefer this solution - but be careful. If I'm not mistaken, since Python dictionaries are not immutable getFromDict has the potential to destroy the caller's dataDict. I would copy.deepcopy(dataDict) first. Of course, (as written) this behavior is desired in the second function.

                          – Dylan F
                          Jun 13 '18 at 3:33














                        • 1





                          I prefer this solution - but be careful. If I'm not mistaken, since Python dictionaries are not immutable getFromDict has the potential to destroy the caller's dataDict. I would copy.deepcopy(dataDict) first. Of course, (as written) this behavior is desired in the second function.

                          – Dylan F
                          Jun 13 '18 at 3:33








                        1




                        1





                        I prefer this solution - but be careful. If I'm not mistaken, since Python dictionaries are not immutable getFromDict has the potential to destroy the caller's dataDict. I would copy.deepcopy(dataDict) first. Of course, (as written) this behavior is desired in the second function.

                        – Dylan F
                        Jun 13 '18 at 3:33





                        I prefer this solution - but be careful. If I'm not mistaken, since Python dictionaries are not immutable getFromDict has the potential to destroy the caller's dataDict. I would copy.deepcopy(dataDict) first. Of course, (as written) this behavior is desired in the second function.

                        – Dylan F
                        Jun 13 '18 at 3:33











                        12














                        Using reduce is clever, but the OP's set method may have issues if the parent keys do not pre-exist in the nested dictionary. Since this is the first SO post I saw for this subject in my google search, I would like to make it slightly better.



                        The set method in ( Setting a value in a nested python dictionary given a list of indices and value ) seems more robust to missing parental keys. To copy it over:



                        def nested_set(dic, keys, value):
                        for key in keys[:-1]:
                        dic = dic.setdefault(key, {})
                        dic[keys[-1]] = value


                        Also, it can be convenient to have a method that traverses the key tree and get all the absolute key paths, for which I have created:



                        def keysInDict(dataDict, parent=):
                        if not isinstance(dataDict, dict):
                        return [tuple(parent)]
                        else:
                        return reduce(list.__add__,
                        [keysInDict(v,parent+[k]) for k,v in dataDict.items()], )


                        One use of it is to convert the nested tree to a pandas DataFrame, using the following code (assuming that all leafs in the nested dictionary have the same depth).



                        def dict_to_df(dataDict):
                        ret =
                        for k in keysInDict(dataDict):
                        v = np.array( getFromDict(dataDict, k), )
                        v = pd.DataFrame(v)
                        v.columns = pd.MultiIndex.from_product(list(k) + [v.columns])
                        ret.append(v)
                        return reduce(pd.DataFrame.join, ret)





                        share|improve this answer






























                          12














                          Using reduce is clever, but the OP's set method may have issues if the parent keys do not pre-exist in the nested dictionary. Since this is the first SO post I saw for this subject in my google search, I would like to make it slightly better.



                          The set method in ( Setting a value in a nested python dictionary given a list of indices and value ) seems more robust to missing parental keys. To copy it over:



                          def nested_set(dic, keys, value):
                          for key in keys[:-1]:
                          dic = dic.setdefault(key, {})
                          dic[keys[-1]] = value


                          Also, it can be convenient to have a method that traverses the key tree and get all the absolute key paths, for which I have created:



                          def keysInDict(dataDict, parent=):
                          if not isinstance(dataDict, dict):
                          return [tuple(parent)]
                          else:
                          return reduce(list.__add__,
                          [keysInDict(v,parent+[k]) for k,v in dataDict.items()], )


                          One use of it is to convert the nested tree to a pandas DataFrame, using the following code (assuming that all leafs in the nested dictionary have the same depth).



                          def dict_to_df(dataDict):
                          ret =
                          for k in keysInDict(dataDict):
                          v = np.array( getFromDict(dataDict, k), )
                          v = pd.DataFrame(v)
                          v.columns = pd.MultiIndex.from_product(list(k) + [v.columns])
                          ret.append(v)
                          return reduce(pd.DataFrame.join, ret)





                          share|improve this answer




























                            12












                            12








                            12







                            Using reduce is clever, but the OP's set method may have issues if the parent keys do not pre-exist in the nested dictionary. Since this is the first SO post I saw for this subject in my google search, I would like to make it slightly better.



                            The set method in ( Setting a value in a nested python dictionary given a list of indices and value ) seems more robust to missing parental keys. To copy it over:



                            def nested_set(dic, keys, value):
                            for key in keys[:-1]:
                            dic = dic.setdefault(key, {})
                            dic[keys[-1]] = value


                            Also, it can be convenient to have a method that traverses the key tree and get all the absolute key paths, for which I have created:



                            def keysInDict(dataDict, parent=):
                            if not isinstance(dataDict, dict):
                            return [tuple(parent)]
                            else:
                            return reduce(list.__add__,
                            [keysInDict(v,parent+[k]) for k,v in dataDict.items()], )


                            One use of it is to convert the nested tree to a pandas DataFrame, using the following code (assuming that all leafs in the nested dictionary have the same depth).



                            def dict_to_df(dataDict):
                            ret =
                            for k in keysInDict(dataDict):
                            v = np.array( getFromDict(dataDict, k), )
                            v = pd.DataFrame(v)
                            v.columns = pd.MultiIndex.from_product(list(k) + [v.columns])
                            ret.append(v)
                            return reduce(pd.DataFrame.join, ret)





                            share|improve this answer















                            Using reduce is clever, but the OP's set method may have issues if the parent keys do not pre-exist in the nested dictionary. Since this is the first SO post I saw for this subject in my google search, I would like to make it slightly better.



                            The set method in ( Setting a value in a nested python dictionary given a list of indices and value ) seems more robust to missing parental keys. To copy it over:



                            def nested_set(dic, keys, value):
                            for key in keys[:-1]:
                            dic = dic.setdefault(key, {})
                            dic[keys[-1]] = value


                            Also, it can be convenient to have a method that traverses the key tree and get all the absolute key paths, for which I have created:



                            def keysInDict(dataDict, parent=):
                            if not isinstance(dataDict, dict):
                            return [tuple(parent)]
                            else:
                            return reduce(list.__add__,
                            [keysInDict(v,parent+[k]) for k,v in dataDict.items()], )


                            One use of it is to convert the nested tree to a pandas DataFrame, using the following code (assuming that all leafs in the nested dictionary have the same depth).



                            def dict_to_df(dataDict):
                            ret =
                            for k in keysInDict(dataDict):
                            v = np.array( getFromDict(dataDict, k), )
                            v = pd.DataFrame(v)
                            v.columns = pd.MultiIndex.from_product(list(k) + [v.columns])
                            ret.append(v)
                            return reduce(pd.DataFrame.join, ret)






                            share|improve this answer














                            share|improve this answer



                            share|improve this answer








                            edited May 23 '17 at 12:26









                            Community

                            11




                            11










                            answered Apr 29 '16 at 4:38









                            eafiteafit

                            27336




                            27336























                                8














                                This library may be helpful: https://github.com/akesterson/dpath-python




                                A python library for accessing and searching dictionaries via
                                /slashed/paths ala xpath



                                Basically it lets you glob over a dictionary as if it were a
                                filesystem.







                                share|improve this answer






























                                  8














                                  This library may be helpful: https://github.com/akesterson/dpath-python




                                  A python library for accessing and searching dictionaries via
                                  /slashed/paths ala xpath



                                  Basically it lets you glob over a dictionary as if it were a
                                  filesystem.







                                  share|improve this answer




























                                    8












                                    8








                                    8







                                    This library may be helpful: https://github.com/akesterson/dpath-python




                                    A python library for accessing and searching dictionaries via
                                    /slashed/paths ala xpath



                                    Basically it lets you glob over a dictionary as if it were a
                                    filesystem.







                                    share|improve this answer















                                    This library may be helpful: https://github.com/akesterson/dpath-python




                                    A python library for accessing and searching dictionaries via
                                    /slashed/paths ala xpath



                                    Basically it lets you glob over a dictionary as if it were a
                                    filesystem.








                                    share|improve this answer














                                    share|improve this answer



                                    share|improve this answer








                                    edited Jul 7 '15 at 16:55









                                    jonrsharpe

                                    77.1k11103208




                                    77.1k11103208










                                    answered Apr 29 '15 at 6:02









                                    DMfllDMfll

                                    99211830




                                    99211830























                                        1














                                        Instead of taking a performance hit each time you want to look up a value, how about you flatten the dictionary once then simply look up the key like b:v:y



                                        def flatten(mydict):
                                        new_dict = {}
                                        for key,value in mydict.items():
                                        if type(value) == dict:
                                        _dict = {':'.join([key, _key]):_value for _key, _value in flatten(value).items()}
                                        new_dict.update(_dict)
                                        else:
                                        new_dict[key]=value
                                        return new_dict

                                        dataDict = {
                                        "a":{
                                        "r": 1,
                                        "s": 2,
                                        "t": 3
                                        },
                                        "b":{
                                        "u": 1,
                                        "v": {
                                        "x": 1,
                                        "y": 2,
                                        "z": 3
                                        },
                                        "w": 3
                                        }
                                        }

                                        flat_dict = flatten(dataDict)
                                        print flat_dict
                                        {'b:w': 3, 'b:u': 1, 'b:v:y': 2, 'b:v:x': 1, 'b:v:z': 3, 'a:r': 1, 'a:s': 2, 'a:t': 3}


                                        This way you can simply look up items using flat_dict['b:v:y'] which will give you 1.



                                        And instead of traversing the dictionary on each lookup, you may be able to speed this up by flattening the dictionary and saving the output so that a lookup from cold start would mean loading up the flattened dictionary and simply performing a key/value lookup with no traversal.






                                        share|improve this answer




























                                          1














                                          Instead of taking a performance hit each time you want to look up a value, how about you flatten the dictionary once then simply look up the key like b:v:y



                                          def flatten(mydict):
                                          new_dict = {}
                                          for key,value in mydict.items():
                                          if type(value) == dict:
                                          _dict = {':'.join([key, _key]):_value for _key, _value in flatten(value).items()}
                                          new_dict.update(_dict)
                                          else:
                                          new_dict[key]=value
                                          return new_dict

                                          dataDict = {
                                          "a":{
                                          "r": 1,
                                          "s": 2,
                                          "t": 3
                                          },
                                          "b":{
                                          "u": 1,
                                          "v": {
                                          "x": 1,
                                          "y": 2,
                                          "z": 3
                                          },
                                          "w": 3
                                          }
                                          }

                                          flat_dict = flatten(dataDict)
                                          print flat_dict
                                          {'b:w': 3, 'b:u': 1, 'b:v:y': 2, 'b:v:x': 1, 'b:v:z': 3, 'a:r': 1, 'a:s': 2, 'a:t': 3}


                                          This way you can simply look up items using flat_dict['b:v:y'] which will give you 1.



                                          And instead of traversing the dictionary on each lookup, you may be able to speed this up by flattening the dictionary and saving the output so that a lookup from cold start would mean loading up the flattened dictionary and simply performing a key/value lookup with no traversal.






                                          share|improve this answer


























                                            1












                                            1








                                            1







                                            Instead of taking a performance hit each time you want to look up a value, how about you flatten the dictionary once then simply look up the key like b:v:y



                                            def flatten(mydict):
                                            new_dict = {}
                                            for key,value in mydict.items():
                                            if type(value) == dict:
                                            _dict = {':'.join([key, _key]):_value for _key, _value in flatten(value).items()}
                                            new_dict.update(_dict)
                                            else:
                                            new_dict[key]=value
                                            return new_dict

                                            dataDict = {
                                            "a":{
                                            "r": 1,
                                            "s": 2,
                                            "t": 3
                                            },
                                            "b":{
                                            "u": 1,
                                            "v": {
                                            "x": 1,
                                            "y": 2,
                                            "z": 3
                                            },
                                            "w": 3
                                            }
                                            }

                                            flat_dict = flatten(dataDict)
                                            print flat_dict
                                            {'b:w': 3, 'b:u': 1, 'b:v:y': 2, 'b:v:x': 1, 'b:v:z': 3, 'a:r': 1, 'a:s': 2, 'a:t': 3}


                                            This way you can simply look up items using flat_dict['b:v:y'] which will give you 1.



                                            And instead of traversing the dictionary on each lookup, you may be able to speed this up by flattening the dictionary and saving the output so that a lookup from cold start would mean loading up the flattened dictionary and simply performing a key/value lookup with no traversal.






                                            share|improve this answer













                                            Instead of taking a performance hit each time you want to look up a value, how about you flatten the dictionary once then simply look up the key like b:v:y



                                            def flatten(mydict):
                                            new_dict = {}
                                            for key,value in mydict.items():
                                            if type(value) == dict:
                                            _dict = {':'.join([key, _key]):_value for _key, _value in flatten(value).items()}
                                            new_dict.update(_dict)
                                            else:
                                            new_dict[key]=value
                                            return new_dict

                                            dataDict = {
                                            "a":{
                                            "r": 1,
                                            "s": 2,
                                            "t": 3
                                            },
                                            "b":{
                                            "u": 1,
                                            "v": {
                                            "x": 1,
                                            "y": 2,
                                            "z": 3
                                            },
                                            "w": 3
                                            }
                                            }

                                            flat_dict = flatten(dataDict)
                                            print flat_dict
                                            {'b:w': 3, 'b:u': 1, 'b:v:y': 2, 'b:v:x': 1, 'b:v:z': 3, 'a:r': 1, 'a:s': 2, 'a:t': 3}


                                            This way you can simply look up items using flat_dict['b:v:y'] which will give you 1.



                                            And instead of traversing the dictionary on each lookup, you may be able to speed this up by flattening the dictionary and saving the output so that a lookup from cold start would mean loading up the flattened dictionary and simply performing a key/value lookup with no traversal.







                                            share|improve this answer












                                            share|improve this answer



                                            share|improve this answer










                                            answered Mar 1 '17 at 2:07









                                            OkezieEOkezieE

                                            3,36711823




                                            3,36711823























                                                1














                                                How about using recursive functions?



                                                To get a value:



                                                def getFromDict(dataDict, maplist):
                                                first, rest = maplist[0], maplist[1:]

                                                if rest:
                                                # if `rest` is not empty, run the function recursively
                                                return getFromDict(dataDict[first], rest)
                                                else:
                                                return dataDict[first]


                                                And to set a value:



                                                def setInDict(dataDict, maplist, value):
                                                first, rest = maplist[0], maplist[1:]

                                                if rest:
                                                try:
                                                if not isinstance(dataDict[first], dict):
                                                # if the key is not a dict, then make it a dict
                                                dataDict[first] = {}
                                                except KeyError:
                                                # if key doesn't exist, create one
                                                dataDict[first] = {}

                                                setInDict(dataDict[first], rest, value)
                                                else:
                                                dataDict[first] = value





                                                share|improve this answer





















                                                • 1





                                                  Great solution!

                                                  – josifoski
                                                  Jun 25 '18 at 14:21
















                                                1














                                                How about using recursive functions?



                                                To get a value:



                                                def getFromDict(dataDict, maplist):
                                                first, rest = maplist[0], maplist[1:]

                                                if rest:
                                                # if `rest` is not empty, run the function recursively
                                                return getFromDict(dataDict[first], rest)
                                                else:
                                                return dataDict[first]


                                                And to set a value:



                                                def setInDict(dataDict, maplist, value):
                                                first, rest = maplist[0], maplist[1:]

                                                if rest:
                                                try:
                                                if not isinstance(dataDict[first], dict):
                                                # if the key is not a dict, then make it a dict
                                                dataDict[first] = {}
                                                except KeyError:
                                                # if key doesn't exist, create one
                                                dataDict[first] = {}

                                                setInDict(dataDict[first], rest, value)
                                                else:
                                                dataDict[first] = value





                                                share|improve this answer





















                                                • 1





                                                  Great solution!

                                                  – josifoski
                                                  Jun 25 '18 at 14:21














                                                1












                                                1








                                                1







                                                How about using recursive functions?



                                                To get a value:



                                                def getFromDict(dataDict, maplist):
                                                first, rest = maplist[0], maplist[1:]

                                                if rest:
                                                # if `rest` is not empty, run the function recursively
                                                return getFromDict(dataDict[first], rest)
                                                else:
                                                return dataDict[first]


                                                And to set a value:



                                                def setInDict(dataDict, maplist, value):
                                                first, rest = maplist[0], maplist[1:]

                                                if rest:
                                                try:
                                                if not isinstance(dataDict[first], dict):
                                                # if the key is not a dict, then make it a dict
                                                dataDict[first] = {}
                                                except KeyError:
                                                # if key doesn't exist, create one
                                                dataDict[first] = {}

                                                setInDict(dataDict[first], rest, value)
                                                else:
                                                dataDict[first] = value





                                                share|improve this answer















                                                How about using recursive functions?



                                                To get a value:



                                                def getFromDict(dataDict, maplist):
                                                first, rest = maplist[0], maplist[1:]

                                                if rest:
                                                # if `rest` is not empty, run the function recursively
                                                return getFromDict(dataDict[first], rest)
                                                else:
                                                return dataDict[first]


                                                And to set a value:



                                                def setInDict(dataDict, maplist, value):
                                                first, rest = maplist[0], maplist[1:]

                                                if rest:
                                                try:
                                                if not isinstance(dataDict[first], dict):
                                                # if the key is not a dict, then make it a dict
                                                dataDict[first] = {}
                                                except KeyError:
                                                # if key doesn't exist, create one
                                                dataDict[first] = {}

                                                setInDict(dataDict[first], rest, value)
                                                else:
                                                dataDict[first] = value






                                                share|improve this answer














                                                share|improve this answer



                                                share|improve this answer








                                                edited Dec 8 '17 at 23:30

























                                                answered Dec 8 '17 at 22:56









                                                xyresxyres

                                                9,32732345




                                                9,32732345








                                                • 1





                                                  Great solution!

                                                  – josifoski
                                                  Jun 25 '18 at 14:21














                                                • 1





                                                  Great solution!

                                                  – josifoski
                                                  Jun 25 '18 at 14:21








                                                1




                                                1





                                                Great solution!

                                                – josifoski
                                                Jun 25 '18 at 14:21





                                                Great solution!

                                                – josifoski
                                                Jun 25 '18 at 14:21











                                                1














                                                Pure Python style, without any import:



                                                def nested_set(element, value, *keys):
                                                if type(element) is not dict:
                                                raise AttributeError('nested_set() expects dict as first argument.')
                                                if len(keys) < 2:
                                                raise AttributeError('nested_set() expects at least three arguments, not enough given.')

                                                _keys = keys[:-1]
                                                _element = element
                                                for key in _keys:
                                                _element = _element[key]
                                                _element[keys[-1]] = value

                                                example = {"foo": { "bar": { "baz": "ok" } } }
                                                keys = ['foo', 'bar']
                                                nested_set(example, "yay", *keys)
                                                print(example)


                                                Output



                                                {'foo': {'bar': 'yay'}}





                                                share|improve this answer




























                                                  1














                                                  Pure Python style, without any import:



                                                  def nested_set(element, value, *keys):
                                                  if type(element) is not dict:
                                                  raise AttributeError('nested_set() expects dict as first argument.')
                                                  if len(keys) < 2:
                                                  raise AttributeError('nested_set() expects at least three arguments, not enough given.')

                                                  _keys = keys[:-1]
                                                  _element = element
                                                  for key in _keys:
                                                  _element = _element[key]
                                                  _element[keys[-1]] = value

                                                  example = {"foo": { "bar": { "baz": "ok" } } }
                                                  keys = ['foo', 'bar']
                                                  nested_set(example, "yay", *keys)
                                                  print(example)


                                                  Output



                                                  {'foo': {'bar': 'yay'}}





                                                  share|improve this answer


























                                                    1












                                                    1








                                                    1







                                                    Pure Python style, without any import:



                                                    def nested_set(element, value, *keys):
                                                    if type(element) is not dict:
                                                    raise AttributeError('nested_set() expects dict as first argument.')
                                                    if len(keys) < 2:
                                                    raise AttributeError('nested_set() expects at least three arguments, not enough given.')

                                                    _keys = keys[:-1]
                                                    _element = element
                                                    for key in _keys:
                                                    _element = _element[key]
                                                    _element[keys[-1]] = value

                                                    example = {"foo": { "bar": { "baz": "ok" } } }
                                                    keys = ['foo', 'bar']
                                                    nested_set(example, "yay", *keys)
                                                    print(example)


                                                    Output



                                                    {'foo': {'bar': 'yay'}}





                                                    share|improve this answer













                                                    Pure Python style, without any import:



                                                    def nested_set(element, value, *keys):
                                                    if type(element) is not dict:
                                                    raise AttributeError('nested_set() expects dict as first argument.')
                                                    if len(keys) < 2:
                                                    raise AttributeError('nested_set() expects at least three arguments, not enough given.')

                                                    _keys = keys[:-1]
                                                    _element = element
                                                    for key in _keys:
                                                    _element = _element[key]
                                                    _element[keys[-1]] = value

                                                    example = {"foo": { "bar": { "baz": "ok" } } }
                                                    keys = ['foo', 'bar']
                                                    nested_set(example, "yay", *keys)
                                                    print(example)


                                                    Output



                                                    {'foo': {'bar': 'yay'}}






                                                    share|improve this answer












                                                    share|improve this answer



                                                    share|improve this answer










                                                    answered Feb 16 '18 at 14:00









                                                    ArountArount

                                                    4,7781025




                                                    4,7781025























                                                        1














                                                        An alternative way if you don't want to raise errors if one of the keys is absent (so that your main code can run without interruption):



                                                        def get_value(self,your_dict,*keys):
                                                        curr_dict_ = your_dict
                                                        for k in keys:
                                                        v = curr_dict.get(k,None)
                                                        if v is None:
                                                        break
                                                        if isinstance(v,dict):
                                                        curr_dict = v
                                                        return v


                                                        In this case, if any of the input keys is not present, None is returned, which can be used as a check in your main code to perform an alternative task.






                                                        share|improve this answer




























                                                          1














                                                          An alternative way if you don't want to raise errors if one of the keys is absent (so that your main code can run without interruption):



                                                          def get_value(self,your_dict,*keys):
                                                          curr_dict_ = your_dict
                                                          for k in keys:
                                                          v = curr_dict.get(k,None)
                                                          if v is None:
                                                          break
                                                          if isinstance(v,dict):
                                                          curr_dict = v
                                                          return v


                                                          In this case, if any of the input keys is not present, None is returned, which can be used as a check in your main code to perform an alternative task.






                                                          share|improve this answer


























                                                            1












                                                            1








                                                            1







                                                            An alternative way if you don't want to raise errors if one of the keys is absent (so that your main code can run without interruption):



                                                            def get_value(self,your_dict,*keys):
                                                            curr_dict_ = your_dict
                                                            for k in keys:
                                                            v = curr_dict.get(k,None)
                                                            if v is None:
                                                            break
                                                            if isinstance(v,dict):
                                                            curr_dict = v
                                                            return v


                                                            In this case, if any of the input keys is not present, None is returned, which can be used as a check in your main code to perform an alternative task.






                                                            share|improve this answer













                                                            An alternative way if you don't want to raise errors if one of the keys is absent (so that your main code can run without interruption):



                                                            def get_value(self,your_dict,*keys):
                                                            curr_dict_ = your_dict
                                                            for k in keys:
                                                            v = curr_dict.get(k,None)
                                                            if v is None:
                                                            break
                                                            if isinstance(v,dict):
                                                            curr_dict = v
                                                            return v


                                                            In this case, if any of the input keys is not present, None is returned, which can be used as a check in your main code to perform an alternative task.







                                                            share|improve this answer












                                                            share|improve this answer



                                                            share|improve this answer










                                                            answered Mar 16 '18 at 1:30









                                                            PulkitPulkit

                                                            415




                                                            415























                                                                1














                                                                How about check and then set dict element without processing all indexes twice?



                                                                Solution:



                                                                def nested_yield(nested, keys_list):
                                                                """
                                                                Get current nested data by send(None) method. Allows change it to Value by calling send(Value) next time
                                                                :param nested: list or dict of lists or dicts
                                                                :param keys_list: list of indexes/keys
                                                                """
                                                                if not len(keys_list): # assign to 1st level list
                                                                if isinstance(nested, list):
                                                                while True:
                                                                nested[:] = yield nested
                                                                else:
                                                                raise IndexError('Only lists can take element without key')


                                                                last_key = keys_list.pop()
                                                                for key in keys_list:
                                                                nested = nested[key]

                                                                while True:
                                                                try:
                                                                nested[last_key] = yield nested[last_key]
                                                                except IndexError as e:
                                                                print('no index {} in {}'.format(last_key, nested))
                                                                yield None


                                                                Example workflow:



                                                                ny = nested_yield(nested_dict, nested_address)
                                                                data_element = ny.send(None)
                                                                if data_element:
                                                                # process element
                                                                ...
                                                                else:
                                                                # extend/update nested data
                                                                ny.send(new_data_element)
                                                                ...
                                                                ny.close()


                                                                Test



                                                                >>> cfg= {'Options': [[1,[0]],[2,[4,[8,16]]],[3,[9]]]}
                                                                ny = nested_yield(cfg, ['Options',1,1,1])
                                                                ny.send(None)
                                                                [8, 16]
                                                                >>> ny.send('Hello!')
                                                                'Hello!'
                                                                >>> cfg
                                                                {'Options': [[1, [0]], [2, [4, 'Hello!']], [3, [9]]]}
                                                                >>> ny.close()





                                                                share|improve this answer






























                                                                  1














                                                                  How about check and then set dict element without processing all indexes twice?



                                                                  Solution:



                                                                  def nested_yield(nested, keys_list):
                                                                  """
                                                                  Get current nested data by send(None) method. Allows change it to Value by calling send(Value) next time
                                                                  :param nested: list or dict of lists or dicts
                                                                  :param keys_list: list of indexes/keys
                                                                  """
                                                                  if not len(keys_list): # assign to 1st level list
                                                                  if isinstance(nested, list):
                                                                  while True:
                                                                  nested[:] = yield nested
                                                                  else:
                                                                  raise IndexError('Only lists can take element without key')


                                                                  last_key = keys_list.pop()
                                                                  for key in keys_list:
                                                                  nested = nested[key]

                                                                  while True:
                                                                  try:
                                                                  nested[last_key] = yield nested[last_key]
                                                                  except IndexError as e:
                                                                  print('no index {} in {}'.format(last_key, nested))
                                                                  yield None


                                                                  Example workflow:



                                                                  ny = nested_yield(nested_dict, nested_address)
                                                                  data_element = ny.send(None)
                                                                  if data_element:
                                                                  # process element
                                                                  ...
                                                                  else:
                                                                  # extend/update nested data
                                                                  ny.send(new_data_element)
                                                                  ...
                                                                  ny.close()


                                                                  Test



                                                                  >>> cfg= {'Options': [[1,[0]],[2,[4,[8,16]]],[3,[9]]]}
                                                                  ny = nested_yield(cfg, ['Options',1,1,1])
                                                                  ny.send(None)
                                                                  [8, 16]
                                                                  >>> ny.send('Hello!')
                                                                  'Hello!'
                                                                  >>> cfg
                                                                  {'Options': [[1, [0]], [2, [4, 'Hello!']], [3, [9]]]}
                                                                  >>> ny.close()





                                                                  share|improve this answer




























                                                                    1












                                                                    1








                                                                    1







                                                                    How about check and then set dict element without processing all indexes twice?



                                                                    Solution:



                                                                    def nested_yield(nested, keys_list):
                                                                    """
                                                                    Get current nested data by send(None) method. Allows change it to Value by calling send(Value) next time
                                                                    :param nested: list or dict of lists or dicts
                                                                    :param keys_list: list of indexes/keys
                                                                    """
                                                                    if not len(keys_list): # assign to 1st level list
                                                                    if isinstance(nested, list):
                                                                    while True:
                                                                    nested[:] = yield nested
                                                                    else:
                                                                    raise IndexError('Only lists can take element without key')


                                                                    last_key = keys_list.pop()
                                                                    for key in keys_list:
                                                                    nested = nested[key]

                                                                    while True:
                                                                    try:
                                                                    nested[last_key] = yield nested[last_key]
                                                                    except IndexError as e:
                                                                    print('no index {} in {}'.format(last_key, nested))
                                                                    yield None


                                                                    Example workflow:



                                                                    ny = nested_yield(nested_dict, nested_address)
                                                                    data_element = ny.send(None)
                                                                    if data_element:
                                                                    # process element
                                                                    ...
                                                                    else:
                                                                    # extend/update nested data
                                                                    ny.send(new_data_element)
                                                                    ...
                                                                    ny.close()


                                                                    Test



                                                                    >>> cfg= {'Options': [[1,[0]],[2,[4,[8,16]]],[3,[9]]]}
                                                                    ny = nested_yield(cfg, ['Options',1,1,1])
                                                                    ny.send(None)
                                                                    [8, 16]
                                                                    >>> ny.send('Hello!')
                                                                    'Hello!'
                                                                    >>> cfg
                                                                    {'Options': [[1, [0]], [2, [4, 'Hello!']], [3, [9]]]}
                                                                    >>> ny.close()





                                                                    share|improve this answer















                                                                    How about check and then set dict element without processing all indexes twice?



                                                                    Solution:



                                                                    def nested_yield(nested, keys_list):
                                                                    """
                                                                    Get current nested data by send(None) method. Allows change it to Value by calling send(Value) next time
                                                                    :param nested: list or dict of lists or dicts
                                                                    :param keys_list: list of indexes/keys
                                                                    """
                                                                    if not len(keys_list): # assign to 1st level list
                                                                    if isinstance(nested, list):
                                                                    while True:
                                                                    nested[:] = yield nested
                                                                    else:
                                                                    raise IndexError('Only lists can take element without key')


                                                                    last_key = keys_list.pop()
                                                                    for key in keys_list:
                                                                    nested = nested[key]

                                                                    while True:
                                                                    try:
                                                                    nested[last_key] = yield nested[last_key]
                                                                    except IndexError as e:
                                                                    print('no index {} in {}'.format(last_key, nested))
                                                                    yield None


                                                                    Example workflow:



                                                                    ny = nested_yield(nested_dict, nested_address)
                                                                    data_element = ny.send(None)
                                                                    if data_element:
                                                                    # process element
                                                                    ...
                                                                    else:
                                                                    # extend/update nested data
                                                                    ny.send(new_data_element)
                                                                    ...
                                                                    ny.close()


                                                                    Test



                                                                    >>> cfg= {'Options': [[1,[0]],[2,[4,[8,16]]],[3,[9]]]}
                                                                    ny = nested_yield(cfg, ['Options',1,1,1])
                                                                    ny.send(None)
                                                                    [8, 16]
                                                                    >>> ny.send('Hello!')
                                                                    'Hello!'
                                                                    >>> cfg
                                                                    {'Options': [[1, [0]], [2, [4, 'Hello!']], [3, [9]]]}
                                                                    >>> ny.close()






                                                                    share|improve this answer














                                                                    share|improve this answer



                                                                    share|improve this answer








                                                                    edited Aug 22 '18 at 3:40

























                                                                    answered Aug 15 '18 at 9:46









                                                                    And0kAnd0k

                                                                    212




                                                                    212























                                                                        0














                                                                        Solved this with recursion:



                                                                        def get(d,l):
                                                                        if len(l)==1: return d[l[0]]
                                                                        return get(d[l[0]],l[1:])


                                                                        Using your example:



                                                                        dataDict = {
                                                                        "a":{
                                                                        "r": 1,
                                                                        "s": 2,
                                                                        "t": 3
                                                                        },
                                                                        "b":{
                                                                        "u": 1,
                                                                        "v": {
                                                                        "x": 1,
                                                                        "y": 2,
                                                                        "z": 3
                                                                        },
                                                                        "w": 3
                                                                        }
                                                                        }
                                                                        maplist1 = ["a", "r"]
                                                                        maplist2 = ["b", "v", "y"]
                                                                        print(get(dataDict, maplist1)) # 1
                                                                        print(get(dataDict, maplist2)) # 2





                                                                        share|improve this answer




























                                                                          0














                                                                          Solved this with recursion:



                                                                          def get(d,l):
                                                                          if len(l)==1: return d[l[0]]
                                                                          return get(d[l[0]],l[1:])


                                                                          Using your example:



                                                                          dataDict = {
                                                                          "a":{
                                                                          "r": 1,
                                                                          "s": 2,
                                                                          "t": 3
                                                                          },
                                                                          "b":{
                                                                          "u": 1,
                                                                          "v": {
                                                                          "x": 1,
                                                                          "y": 2,
                                                                          "z": 3
                                                                          },
                                                                          "w": 3
                                                                          }
                                                                          }
                                                                          maplist1 = ["a", "r"]
                                                                          maplist2 = ["b", "v", "y"]
                                                                          print(get(dataDict, maplist1)) # 1
                                                                          print(get(dataDict, maplist2)) # 2





                                                                          share|improve this answer


























                                                                            0












                                                                            0








                                                                            0







                                                                            Solved this with recursion:



                                                                            def get(d,l):
                                                                            if len(l)==1: return d[l[0]]
                                                                            return get(d[l[0]],l[1:])


                                                                            Using your example:



                                                                            dataDict = {
                                                                            "a":{
                                                                            "r": 1,
                                                                            "s": 2,
                                                                            "t": 3
                                                                            },
                                                                            "b":{
                                                                            "u": 1,
                                                                            "v": {
                                                                            "x": 1,
                                                                            "y": 2,
                                                                            "z": 3
                                                                            },
                                                                            "w": 3
                                                                            }
                                                                            }
                                                                            maplist1 = ["a", "r"]
                                                                            maplist2 = ["b", "v", "y"]
                                                                            print(get(dataDict, maplist1)) # 1
                                                                            print(get(dataDict, maplist2)) # 2





                                                                            share|improve this answer













                                                                            Solved this with recursion:



                                                                            def get(d,l):
                                                                            if len(l)==1: return d[l[0]]
                                                                            return get(d[l[0]],l[1:])


                                                                            Using your example:



                                                                            dataDict = {
                                                                            "a":{
                                                                            "r": 1,
                                                                            "s": 2,
                                                                            "t": 3
                                                                            },
                                                                            "b":{
                                                                            "u": 1,
                                                                            "v": {
                                                                            "x": 1,
                                                                            "y": 2,
                                                                            "z": 3
                                                                            },
                                                                            "w": 3
                                                                            }
                                                                            }
                                                                            maplist1 = ["a", "r"]
                                                                            maplist2 = ["b", "v", "y"]
                                                                            print(get(dataDict, maplist1)) # 1
                                                                            print(get(dataDict, maplist2)) # 2






                                                                            share|improve this answer












                                                                            share|improve this answer



                                                                            share|improve this answer










                                                                            answered Mar 5 '18 at 13:38









                                                                            Poh Zi HowPoh Zi How

                                                                            3303622




                                                                            3303622























                                                                                0














                                                                                If you also want the ability to work with arbitrary json including nested lists and dicts, and nicely handle invalid lookup paths, here's my solution:



                                                                                from functools import reduce


                                                                                def get_furthest(s, path):
                                                                                '''
                                                                                Gets the furthest value along a given key path in a subscriptable structure.

                                                                                subscriptable, list -> any
                                                                                :param s: the subscriptable structure to examine
                                                                                :param path: the lookup path to follow
                                                                                :return: a tuple of the value at the furthest valid key, and whether the full path is valid
                                                                                '''

                                                                                def step_key(acc, key):
                                                                                s = acc[0]
                                                                                if isinstance(s, str):
                                                                                return (s, False)
                                                                                try:
                                                                                return (s[key], acc[1])
                                                                                except LookupError:
                                                                                return (s, False)

                                                                                return reduce(step_key, path, (s, True))


                                                                                def get_val(s, path):
                                                                                val, successful = get_furthest(s, path)
                                                                                if successful:
                                                                                return val
                                                                                else:
                                                                                raise LookupError('Invalid lookup path: {}'.format(path))


                                                                                def set_val(s, path, value):
                                                                                get_val(s, path[:-1])[path[-1]] = value





                                                                                share|improve this answer




























                                                                                  0














                                                                                  If you also want the ability to work with arbitrary json including nested lists and dicts, and nicely handle invalid lookup paths, here's my solution:



                                                                                  from functools import reduce


                                                                                  def get_furthest(s, path):
                                                                                  '''
                                                                                  Gets the furthest value along a given key path in a subscriptable structure.

                                                                                  subscriptable, list -> any
                                                                                  :param s: the subscriptable structure to examine
                                                                                  :param path: the lookup path to follow
                                                                                  :return: a tuple of the value at the furthest valid key, and whether the full path is valid
                                                                                  '''

                                                                                  def step_key(acc, key):
                                                                                  s = acc[0]
                                                                                  if isinstance(s, str):
                                                                                  return (s, False)
                                                                                  try:
                                                                                  return (s[key], acc[1])
                                                                                  except LookupError:
                                                                                  return (s, False)

                                                                                  return reduce(step_key, path, (s, True))


                                                                                  def get_val(s, path):
                                                                                  val, successful = get_furthest(s, path)
                                                                                  if successful:
                                                                                  return val
                                                                                  else:
                                                                                  raise LookupError('Invalid lookup path: {}'.format(path))


                                                                                  def set_val(s, path, value):
                                                                                  get_val(s, path[:-1])[path[-1]] = value





                                                                                  share|improve this answer


























                                                                                    0












                                                                                    0








                                                                                    0







                                                                                    If you also want the ability to work with arbitrary json including nested lists and dicts, and nicely handle invalid lookup paths, here's my solution:



                                                                                    from functools import reduce


                                                                                    def get_furthest(s, path):
                                                                                    '''
                                                                                    Gets the furthest value along a given key path in a subscriptable structure.

                                                                                    subscriptable, list -> any
                                                                                    :param s: the subscriptable structure to examine
                                                                                    :param path: the lookup path to follow
                                                                                    :return: a tuple of the value at the furthest valid key, and whether the full path is valid
                                                                                    '''

                                                                                    def step_key(acc, key):
                                                                                    s = acc[0]
                                                                                    if isinstance(s, str):
                                                                                    return (s, False)
                                                                                    try:
                                                                                    return (s[key], acc[1])
                                                                                    except LookupError:
                                                                                    return (s, False)

                                                                                    return reduce(step_key, path, (s, True))


                                                                                    def get_val(s, path):
                                                                                    val, successful = get_furthest(s, path)
                                                                                    if successful:
                                                                                    return val
                                                                                    else:
                                                                                    raise LookupError('Invalid lookup path: {}'.format(path))


                                                                                    def set_val(s, path, value):
                                                                                    get_val(s, path[:-1])[path[-1]] = value





                                                                                    share|improve this answer













                                                                                    If you also want the ability to work with arbitrary json including nested lists and dicts, and nicely handle invalid lookup paths, here's my solution:



                                                                                    from functools import reduce


                                                                                    def get_furthest(s, path):
                                                                                    '''
                                                                                    Gets the furthest value along a given key path in a subscriptable structure.

                                                                                    subscriptable, list -> any
                                                                                    :param s: the subscriptable structure to examine
                                                                                    :param path: the lookup path to follow
                                                                                    :return: a tuple of the value at the furthest valid key, and whether the full path is valid
                                                                                    '''

                                                                                    def step_key(acc, key):
                                                                                    s = acc[0]
                                                                                    if isinstance(s, str):
                                                                                    return (s, False)
                                                                                    try:
                                                                                    return (s[key], acc[1])
                                                                                    except LookupError:
                                                                                    return (s, False)

                                                                                    return reduce(step_key, path, (s, True))


                                                                                    def get_val(s, path):
                                                                                    val, successful = get_furthest(s, path)
                                                                                    if successful:
                                                                                    return val
                                                                                    else:
                                                                                    raise LookupError('Invalid lookup path: {}'.format(path))


                                                                                    def set_val(s, path, value):
                                                                                    get_val(s, path[:-1])[path[-1]] = value






                                                                                    share|improve this answer












                                                                                    share|improve this answer



                                                                                    share|improve this answer










                                                                                    answered Mar 6 '18 at 21:30









                                                                                    Grant PalmerGrant Palmer

                                                                                    885




                                                                                    885























                                                                                        0














                                                                                        It's satisfying to see these answers for having two static methods for setting & getting nested attributes. These solutions are way better than using nested trees https://gist.github.com/hrldcpr/2012250



                                                                                        Here's my implementation.



                                                                                        Usage:



                                                                                        To set nested attribute call sattr(my_dict, 1, 2, 3, 5) is equal to my_dict[1][2][3][4]=5



                                                                                        To get a nested attribute call gattr(my_dict, 1, 2)



                                                                                        def gattr(d, *attrs):
                                                                                        """
                                                                                        This method receives a dict and list of attributes to return the innermost value of the give dict
                                                                                        """
                                                                                        try:
                                                                                        for at in attrs:
                                                                                        d = d[at]
                                                                                        return d
                                                                                        except:
                                                                                        return None


                                                                                        def sattr(d, *attrs):
                                                                                        """
                                                                                        Adds "val" to dict in the hierarchy mentioned via *attrs
                                                                                        For ex:
                                                                                        sattr(animals, "cat", "leg","fingers", 4) is equivalent to animals["cat"]["leg"]["fingers"]=4
                                                                                        This method creates necessary objects until it reaches the final depth
                                                                                        This behaviour is also known as autovivification and plenty of implementation are around
                                                                                        This implementation addresses the corner case of replacing existing primitives
                                                                                        https://gist.github.com/hrldcpr/2012250#gistcomment-1779319
                                                                                        """
                                                                                        for attr in attrs[:-2]:
                                                                                        # If such key is not found or the value is primitive supply an empty dict
                                                                                        if d.get(attr) is None or isinstance(d.get(attr), dict):
                                                                                        d[attr] = {}
                                                                                        d = d[attr]
                                                                                        d[attrs[-2]] = attrs[-1]





                                                                                        share|improve this answer




























                                                                                          0














                                                                                          It's satisfying to see these answers for having two static methods for setting & getting nested attributes. These solutions are way better than using nested trees https://gist.github.com/hrldcpr/2012250



                                                                                          Here's my implementation.



                                                                                          Usage:



                                                                                          To set nested attribute call sattr(my_dict, 1, 2, 3, 5) is equal to my_dict[1][2][3][4]=5



                                                                                          To get a nested attribute call gattr(my_dict, 1, 2)



                                                                                          def gattr(d, *attrs):
                                                                                          """
                                                                                          This method receives a dict and list of attributes to return the innermost value of the give dict
                                                                                          """
                                                                                          try:
                                                                                          for at in attrs:
                                                                                          d = d[at]
                                                                                          return d
                                                                                          except:
                                                                                          return None


                                                                                          def sattr(d, *attrs):
                                                                                          """
                                                                                          Adds "val" to dict in the hierarchy mentioned via *attrs
                                                                                          For ex:
                                                                                          sattr(animals, "cat", "leg","fingers", 4) is equivalent to animals["cat"]["leg"]["fingers"]=4
                                                                                          This method creates necessary objects until it reaches the final depth
                                                                                          This behaviour is also known as autovivification and plenty of implementation are around
                                                                                          This implementation addresses the corner case of replacing existing primitives
                                                                                          https://gist.github.com/hrldcpr/2012250#gistcomment-1779319
                                                                                          """
                                                                                          for attr in attrs[:-2]:
                                                                                          # If such key is not found or the value is primitive supply an empty dict
                                                                                          if d.get(attr) is None or isinstance(d.get(attr), dict):
                                                                                          d[attr] = {}
                                                                                          d = d[attr]
                                                                                          d[attrs[-2]] = attrs[-1]





                                                                                          share|improve this answer


























                                                                                            0












                                                                                            0








                                                                                            0







                                                                                            It's satisfying to see these answers for having two static methods for setting & getting nested attributes. These solutions are way better than using nested trees https://gist.github.com/hrldcpr/2012250



                                                                                            Here's my implementation.



                                                                                            Usage:



                                                                                            To set nested attribute call sattr(my_dict, 1, 2, 3, 5) is equal to my_dict[1][2][3][4]=5



                                                                                            To get a nested attribute call gattr(my_dict, 1, 2)



                                                                                            def gattr(d, *attrs):
                                                                                            """
                                                                                            This method receives a dict and list of attributes to return the innermost value of the give dict
                                                                                            """
                                                                                            try:
                                                                                            for at in attrs:
                                                                                            d = d[at]
                                                                                            return d
                                                                                            except:
                                                                                            return None


                                                                                            def sattr(d, *attrs):
                                                                                            """
                                                                                            Adds "val" to dict in the hierarchy mentioned via *attrs
                                                                                            For ex:
                                                                                            sattr(animals, "cat", "leg","fingers", 4) is equivalent to animals["cat"]["leg"]["fingers"]=4
                                                                                            This method creates necessary objects until it reaches the final depth
                                                                                            This behaviour is also known as autovivification and plenty of implementation are around
                                                                                            This implementation addresses the corner case of replacing existing primitives
                                                                                            https://gist.github.com/hrldcpr/2012250#gistcomment-1779319
                                                                                            """
                                                                                            for attr in attrs[:-2]:
                                                                                            # If such key is not found or the value is primitive supply an empty dict
                                                                                            if d.get(attr) is None or isinstance(d.get(attr), dict):
                                                                                            d[attr] = {}
                                                                                            d = d[attr]
                                                                                            d[attrs[-2]] = attrs[-1]





                                                                                            share|improve this answer













                                                                                            It's satisfying to see these answers for having two static methods for setting & getting nested attributes. These solutions are way better than using nested trees https://gist.github.com/hrldcpr/2012250



                                                                                            Here's my implementation.



                                                                                            Usage:



                                                                                            To set nested attribute call sattr(my_dict, 1, 2, 3, 5) is equal to my_dict[1][2][3][4]=5



                                                                                            To get a nested attribute call gattr(my_dict, 1, 2)



                                                                                            def gattr(d, *attrs):
                                                                                            """
                                                                                            This method receives a dict and list of attributes to return the innermost value of the give dict
                                                                                            """
                                                                                            try:
                                                                                            for at in attrs:
                                                                                            d = d[at]
                                                                                            return d
                                                                                            except:
                                                                                            return None


                                                                                            def sattr(d, *attrs):
                                                                                            """
                                                                                            Adds "val" to dict in the hierarchy mentioned via *attrs
                                                                                            For ex:
                                                                                            sattr(animals, "cat", "leg","fingers", 4) is equivalent to animals["cat"]["leg"]["fingers"]=4
                                                                                            This method creates necessary objects until it reaches the final depth
                                                                                            This behaviour is also known as autovivification and plenty of implementation are around
                                                                                            This implementation addresses the corner case of replacing existing primitives
                                                                                            https://gist.github.com/hrldcpr/2012250#gistcomment-1779319
                                                                                            """
                                                                                            for attr in attrs[:-2]:
                                                                                            # If such key is not found or the value is primitive supply an empty dict
                                                                                            if d.get(attr) is None or isinstance(d.get(attr), dict):
                                                                                            d[attr] = {}
                                                                                            d = d[attr]
                                                                                            d[attrs[-2]] = attrs[-1]






                                                                                            share|improve this answer












                                                                                            share|improve this answer



                                                                                            share|improve this answer










                                                                                            answered Dec 3 '18 at 0:56









                                                                                            nehemiahnehemiah

                                                                                            4,01722549




                                                                                            4,01722549























                                                                                                0














                                                                                                Very late to the party, but posting in case this may help someone in the future. For my use case, the following function worked the best. Works to pull any data type out of dictionary



                                                                                                dict is the dictionary containing our value



                                                                                                list is a list of "steps" towards our value



                                                                                                def getnestedvalue(dict, list):

                                                                                                length = len(list)
                                                                                                try:
                                                                                                for depth, key in enumerate(list):
                                                                                                if depth == length - 1:
                                                                                                output = dict[key]
                                                                                                return output
                                                                                                dict = dict[key]
                                                                                                except (KeyError, TypeError):
                                                                                                return None

                                                                                                return None





                                                                                                share|improve this answer




























                                                                                                  0














                                                                                                  Very late to the party, but posting in case this may help someone in the future. For my use case, the following function worked the best. Works to pull any data type out of dictionary



                                                                                                  dict is the dictionary containing our value



                                                                                                  list is a list of "steps" towards our value



                                                                                                  def getnestedvalue(dict, list):

                                                                                                  length = len(list)
                                                                                                  try:
                                                                                                  for depth, key in enumerate(list):
                                                                                                  if depth == length - 1:
                                                                                                  output = dict[key]
                                                                                                  return output
                                                                                                  dict = dict[key]
                                                                                                  except (KeyError, TypeError):
                                                                                                  return None

                                                                                                  return None





                                                                                                  share|improve this answer


























                                                                                                    0












                                                                                                    0








                                                                                                    0







                                                                                                    Very late to the party, but posting in case this may help someone in the future. For my use case, the following function worked the best. Works to pull any data type out of dictionary



                                                                                                    dict is the dictionary containing our value



                                                                                                    list is a list of "steps" towards our value



                                                                                                    def getnestedvalue(dict, list):

                                                                                                    length = len(list)
                                                                                                    try:
                                                                                                    for depth, key in enumerate(list):
                                                                                                    if depth == length - 1:
                                                                                                    output = dict[key]
                                                                                                    return output
                                                                                                    dict = dict[key]
                                                                                                    except (KeyError, TypeError):
                                                                                                    return None

                                                                                                    return None





                                                                                                    share|improve this answer













                                                                                                    Very late to the party, but posting in case this may help someone in the future. For my use case, the following function worked the best. Works to pull any data type out of dictionary



                                                                                                    dict is the dictionary containing our value



                                                                                                    list is a list of "steps" towards our value



                                                                                                    def getnestedvalue(dict, list):

                                                                                                    length = len(list)
                                                                                                    try:
                                                                                                    for depth, key in enumerate(list):
                                                                                                    if depth == length - 1:
                                                                                                    output = dict[key]
                                                                                                    return output
                                                                                                    dict = dict[key]
                                                                                                    except (KeyError, TypeError):
                                                                                                    return None

                                                                                                    return None






                                                                                                    share|improve this answer












                                                                                                    share|improve this answer



                                                                                                    share|improve this answer










                                                                                                    answered Dec 9 '18 at 8:43









                                                                                                    Jack CaseyJack Casey

                                                                                                    285




                                                                                                    285























                                                                                                        0














                                                                                                        a method for concatenating strings:



                                                                                                        def get_sub_object_from_path(dict_name, map_list):
                                                                                                        for i in map_list:
                                                                                                        _string = "['%s']" % i
                                                                                                        dict_name += _string
                                                                                                        value = eval(dict_name)
                                                                                                        return value
                                                                                                        #Sample:
                                                                                                        _dict = {'new': 'person', 'time': {'for': 'one'}}
                                                                                                        map_list = ['time', 'for']
                                                                                                        print get_sub_object_from_path("_dict",map_list)
                                                                                                        #Output:
                                                                                                        #one





                                                                                                        share|improve this answer




























                                                                                                          0














                                                                                                          a method for concatenating strings:



                                                                                                          def get_sub_object_from_path(dict_name, map_list):
                                                                                                          for i in map_list:
                                                                                                          _string = "['%s']" % i
                                                                                                          dict_name += _string
                                                                                                          value = eval(dict_name)
                                                                                                          return value
                                                                                                          #Sample:
                                                                                                          _dict = {'new': 'person', 'time': {'for': 'one'}}
                                                                                                          map_list = ['time', 'for']
                                                                                                          print get_sub_object_from_path("_dict",map_list)
                                                                                                          #Output:
                                                                                                          #one





                                                                                                          share|improve this answer


























                                                                                                            0












                                                                                                            0








                                                                                                            0







                                                                                                            a method for concatenating strings:



                                                                                                            def get_sub_object_from_path(dict_name, map_list):
                                                                                                            for i in map_list:
                                                                                                            _string = "['%s']" % i
                                                                                                            dict_name += _string
                                                                                                            value = eval(dict_name)
                                                                                                            return value
                                                                                                            #Sample:
                                                                                                            _dict = {'new': 'person', 'time': {'for': 'one'}}
                                                                                                            map_list = ['time', 'for']
                                                                                                            print get_sub_object_from_path("_dict",map_list)
                                                                                                            #Output:
                                                                                                            #one





                                                                                                            share|improve this answer













                                                                                                            a method for concatenating strings:



                                                                                                            def get_sub_object_from_path(dict_name, map_list):
                                                                                                            for i in map_list:
                                                                                                            _string = "['%s']" % i
                                                                                                            dict_name += _string
                                                                                                            value = eval(dict_name)
                                                                                                            return value
                                                                                                            #Sample:
                                                                                                            _dict = {'new': 'person', 'time': {'for': 'one'}}
                                                                                                            map_list = ['time', 'for']
                                                                                                            print get_sub_object_from_path("_dict",map_list)
                                                                                                            #Output:
                                                                                                            #one






                                                                                                            share|improve this answer












                                                                                                            share|improve this answer



                                                                                                            share|improve this answer










                                                                                                            answered Dec 29 '18 at 5:10









                                                                                                            lucaslucas

                                                                                                            11




                                                                                                            11






























                                                                                                                draft saved

                                                                                                                draft discarded




















































                                                                                                                Thanks for contributing an answer to Stack Overflow!


                                                                                                                • Please be sure to answer the question. Provide details and share your research!

                                                                                                                But avoid



                                                                                                                • Asking for help, clarification, or responding to other answers.

                                                                                                                • Making statements based on opinion; back them up with references or personal experience.


                                                                                                                To learn more, see our tips on writing great answers.




                                                                                                                draft saved


                                                                                                                draft discarded














                                                                                                                StackExchange.ready(
                                                                                                                function () {
                                                                                                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f14692690%2faccess-nested-dictionary-items-via-a-list-of-keys%23new-answer', 'question_page');
                                                                                                                }
                                                                                                                );

                                                                                                                Post as a guest















                                                                                                                Required, but never shown





















































                                                                                                                Required, but never shown














                                                                                                                Required, but never shown












                                                                                                                Required, but never shown







                                                                                                                Required, but never shown

































                                                                                                                Required, but never shown














                                                                                                                Required, but never shown












                                                                                                                Required, but never shown







                                                                                                                Required, but never shown







                                                                                                                Popular posts from this blog

                                                                                                                Monofisismo

                                                                                                                Angular Downloading a file using contenturl with Basic Authentication

                                                                                                                Olmecas