text: # AUTOGENERATED! DO NOT EDIT! File to edit: nb_utils.dict.ipynb (unless otherwise specified).
  
  __all__ = ['apply_to_vals', 'remove_key', 'flatten_intra', 'collapse_helper', 'collapse_once', 'collapse_all',
             'flatten_inter']
  
  # Cell
  import pandas as pd
  import numpy as np
  
  # Cell
  def apply_to_vals(nested_dict, func, use_key:bool=False):
      '''Applies a function "func" to all non-dict values of a (nested) dictionary'''
      new_dict = dict()
  
      for k, v in nested_dict.items():
          if isinstance(v, dict):
              new_dict[k] = apply_to_vals(v, func, use_key)
          else:
              if use_key:
                  new_dict[k] = func(k, v)
              else:
                  new_dict[k] = func(v)
  
      return new_dict
  
  # Cell
  def remove_key(nested_dict, key):
      '''Removes a key, together with its value, from a (nested) dict)'''
      new_dict = dict()
  
      for k, v in nested_dict.items():
          if isinstance(v, dict):
              if k!=key:
                  new_dict[k] = remove_key(v, key)
          else:
              if k!=key:
                  new_dict[k] = v
      return new_dict
  
  # Cell
  def flatten_intra(nested_dict, include_key:bool=False):
      new_dict = dict()
      intra_li = list()
  
      for k, v in nested_dict.items():
          if isinstance(v, dict):
              new_dict[k] = flatten_intra(v, include_key)
          else:
              if include_key:
                  intra_li.append((k, v))
              else:
                  intra_li.append(v)
  
      if intra_li:
          return intra_li
      else:
          return new_dict
  
  # Cell
  def collapse_helper(nested_dict, only_once:bool=True):
  
      new_dict = dict()
      new_dict["_collapsed_once"] = False
  
      for k, v in nested_dict.items():
          if isinstance(v, dict) and len(v)>1:
              new_dict[k] = collapse_helper(v)
          else: # either item, empty dict or 1-item dict
              if isinstance(v, dict) and len(v)==1:
                  new_dict[k] = list(v.values())[0]
                  new_dict["_collapsed_once"] = True
              elif isinstance(v, dict) and len(v)==0:
                  new_dict["_collapsed_once"] = True
              else:
                  new_dict[k] = v
  
      if only_once:
          new_dict.pop("_collapsed_once", None)
  
      return new_dict
  
  def collapse_once(nested_dict):
      return collapse_helper(nested_dict, only_once=True)
  
  def collapse_all(nested_dict):
      new_dict = nested_dict.copy()
  
      flag = True
      while flag:
          new_dict.pop("_collapsed_once", None)
          new_dict = collapse_helper(new_dict, only_once=False)
          flag = new_dict.get("_collapsed_once")
  
      new_dict.pop("_collapsed_once", None)
      return new_dict
  
  # Cell
  def flatten_inter(nested_dict, include_key:bool=False):
  
      def flatten_inter_helper(nested_dict):
          new_dict = dict()
          intra_dict = dict()
  
          for k, v in nested_dict.items():
              if isinstance(v, dict):
                  new_dict[k] = flatten_inter_helper(v)
              else:
                  for kk, vv in v:
                      if intra_dict.get(kk) is None:
                          intra_dict[kk] = [(k, vv),] if include_key else [vv,]
                      else:
                          if include_key:
                              intra_dict[kk].append((k,vv))
                          else:
                              intra_dict[kk].append(vv)
  
          if intra_dict:
              return intra_dict
          else:
              return new_dict
  
      temp = flatten_intra(nested_dict, include_key=True)
      return flatten_inter_helper(temp)
  