nodes: 0, module; 1, function_definition; 2, function_name:hierarchical_map_vals; 3, parameters; 4, block; 5, identifier:func; 6, identifier:node; 7, default_parameter; 8, default_parameter; 9, expression_statement; 10, comment:#if not isinstance(node, dict):; 11, if_statement; 12, identifier:max_depth; 13, None; 14, identifier:depth; 15, integer:0; 16, comment:"""
      node is a dict tree like structure with leaves of type list
  
      TODO: move to util_dict
  
      CommandLine:
          python -m utool.util_dict --exec-hierarchical_map_vals
  
      Example:
          >>> # ENABLE_DOCTEST
          >>> from utool.util_dict import *  # NOQA
          >>> import utool as ut
          >>> item_list     = [1, 2, 3, 4, 5, 6, 7, 8]
          >>> groupids_list = [[1, 2, 1, 2, 1, 2, 1, 2], [3, 2, 2, 2, 3, 1, 1, 1]]
          >>> tree = ut.hierarchical_group_items(item_list, groupids_list)
          >>> len_tree = ut.hierarchical_map_vals(len, tree)
          >>> result = ('len_tree = ' + ut.repr4(len_tree, nl=1))
          >>> print(result)
          len_tree = {
              1: {1: 1, 2: 1, 3: 2},
              2: {1: 2, 2: 2},
          }
  
      Example1:
          >>> # DISABLE_DOCTEST
          >>> # UNSTABLE_DOCTEST
          >>> from utool.util_dict import *  # NOQA
          >>> import utool as ut
          >>> depth = 4
          >>> item_list = list(range(2 ** (depth + 1)))
          >>> num = len(item_list) // 2
          >>> groupids_list = []
          >>> total = 0
          >>> for level in range(depth):
          ...     num2 = len(item_list) // int((num * 2))
          ...     #nonflat_levelids = [([total + 2 * x + 1] * num + [total + 2 * x + 2] * num) for x in range(num2)]
          ...     nonflat_levelids = [([1] * num + [2] * num) for x in range(num2)]
          ...     levelids = ut.flatten(nonflat_levelids)
          ...     groupids_list.append(levelids)
          ...     total += num2 * 2
          ...     num //= 2
          >>> print('groupids_list = %s' % (ut.repr4(groupids_list, nl=1),))
          >>> print('depth = %r' % (len(groupids_list),))
          >>> tree = ut.hierarchical_group_items(item_list, groupids_list)
          >>> print('tree = ' + ut.repr4(tree, nl=None))
          >>> flat_tree_values = list(ut.iflatten_dict_values(tree))
          >>> assert sorted(flat_tree_values) == sorted(item_list)
          >>> print('flat_tree_values = ' + str(flat_tree_values))
          >>> #print('flat_tree_keys = ' + str(list(ut.iflatten_dict_keys(tree))))
          >>> #print('iflatten_dict_items = ' + str(list(ut.iflatten_dict_items(tree))))
          >>> len_tree = ut.hierarchical_map_vals(len, tree, max_depth=4)
          >>> result = ('len_tree = ' + ut.repr4(len_tree, nl=None))
          >>> print(result)
  
      """; 17, not_operator; 18, block; 19, elif_clause; 20, else_clause; 21, call; 22, return_statement; 23, boolean_operator; 24, comment:#return func(node); 25, block; 26, comment:# recursion; 27, comment:#return {key: hierarchical_map_vals(func, val, max_depth, depth + 1) for key, val in six.iteritems(node)}; 28, comment:#keyval_list = [(key, hierarchical_map_vals(func, val, max_depth, depth + 1)) for key, val in six.iteritems(node)]; 29, block; 30, identifier:hasattr; 31, argument_list; 32, call; 33, comparison_operator:max_depth is not None; 34, comparison_operator:depth >= max_depth; 35, return_statement; 36, comment:#return {key: func(val) for key, val in six.iteritems(node)}; 37, expression_statement; 38, if_statement; 39, identifier:node; 40, string; 41, identifier:func; 42, argument_list; 43, identifier:max_depth; 44, None; 45, identifier:depth; 46, identifier:max_depth; 47, call; 48, assignment; 49, call; 50, block; 51, else_clause; 52, string_content:items; 53, identifier:node; 54, identifier:map_dict_vals; 55, argument_list; 56, identifier:keyval_list; 57, list_comprehension; 58, identifier:isinstance; 59, argument_list; 60, return_statement; 61, block; 62, identifier:func; 63, identifier:node; 64, tuple; 65, for_in_clause; 66, identifier:node; 67, identifier:OrderedDict; 68, call; 69, return_statement; 70, identifier:key; 71, call; 72, pattern_list; 73, call; 74, identifier:OrderedDict; 75, argument_list; 76, call; 77, identifier:hierarchical_map_vals; 78, argument_list; 79, identifier:key; 80, identifier:val; 81, attribute; 82, argument_list; 83, identifier:keyval_list; 84, identifier:dict; 85, argument_list; 86, identifier:func; 87, identifier:val; 88, identifier:max_depth; 89, binary_operator:depth + 1; 90, identifier:node; 91, identifier:items; 92, identifier:keyval_list; 93, identifier:depth; 94, integer:1
code: def hierarchical_map_vals(func, node, max_depth=None, depth=0):
      """
      node is a dict tree like structure with leaves of type list
  
      TODO: move to util_dict
  
      CommandLine:
          python -m utool.util_dict --exec-hierarchical_map_vals
  
      Example:
          >>> # ENABLE_DOCTEST
          >>> from utool.util_dict import *  # NOQA
          >>> import utool as ut
          >>> item_list     = [1, 2, 3, 4, 5, 6, 7, 8]
          >>> groupids_list = [[1, 2, 1, 2, 1, 2, 1, 2], [3, 2, 2, 2, 3, 1, 1, 1]]
          >>> tree = ut.hierarchical_group_items(item_list, groupids_list)
          >>> len_tree = ut.hierarchical_map_vals(len, tree)
          >>> result = ('len_tree = ' + ut.repr4(len_tree, nl=1))
          >>> print(result)
          len_tree = {
              1: {1: 1, 2: 1, 3: 2},
              2: {1: 2, 2: 2},
          }
  
      Example1:
          >>> # DISABLE_DOCTEST
          >>> # UNSTABLE_DOCTEST
          >>> from utool.util_dict import *  # NOQA
          >>> import utool as ut
          >>> depth = 4
          >>> item_list = list(range(2 ** (depth + 1)))
          >>> num = len(item_list) // 2
          >>> groupids_list = []
          >>> total = 0
          >>> for level in range(depth):
          ...     num2 = len(item_list) // int((num * 2))
          ...     #nonflat_levelids = [([total + 2 * x + 1] * num + [total + 2 * x + 2] * num) for x in range(num2)]
          ...     nonflat_levelids = [([1] * num + [2] * num) for x in range(num2)]
          ...     levelids = ut.flatten(nonflat_levelids)
          ...     groupids_list.append(levelids)
          ...     total += num2 * 2
          ...     num //= 2
          >>> print('groupids_list = %s' % (ut.repr4(groupids_list, nl=1),))
          >>> print('depth = %r' % (len(groupids_list),))
          >>> tree = ut.hierarchical_group_items(item_list, groupids_list)
          >>> print('tree = ' + ut.repr4(tree, nl=None))
          >>> flat_tree_values = list(ut.iflatten_dict_values(tree))
          >>> assert sorted(flat_tree_values) == sorted(item_list)
          >>> print('flat_tree_values = ' + str(flat_tree_values))
          >>> #print('flat_tree_keys = ' + str(list(ut.iflatten_dict_keys(tree))))
          >>> #print('iflatten_dict_items = ' + str(list(ut.iflatten_dict_items(tree))))
          >>> len_tree = ut.hierarchical_map_vals(len, tree, max_depth=4)
          >>> result = ('len_tree = ' + ut.repr4(len_tree, nl=None))
          >>> print(result)
  
      """
      #if not isinstance(node, dict):
      if not hasattr(node, 'items'):
          return func(node)
      elif max_depth is not None and depth >= max_depth:
          #return func(node)
          return map_dict_vals(func, node)
          #return {key: func(val) for key, val in six.iteritems(node)}
      else:
          # recursion
          #return {key: hierarchical_map_vals(func, val, max_depth, depth + 1) for key, val in six.iteritems(node)}
          #keyval_list = [(key, hierarchical_map_vals(func, val, max_depth, depth + 1)) for key, val in six.iteritems(node)]
          keyval_list = [(key, hierarchical_map_vals(func, val, max_depth, depth + 1)) for key, val in node.items()]
          if isinstance(node, OrderedDict):
              return OrderedDict(keyval_list)
          else:
              return dict(keyval_list)