docs: 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)