def filter_(predicate, *structures, **kwargs):
  # pylint: disable=differing-param-doc,missing-param-doc, too-many-branches
  """Select elements of a nested structure based on a predicate function.

  If multiple structures are provided as input, their structure must match and
  the function will be applied to corresponding groups of elements. The nested
  structure can consist of any combination of lists, tuples, and dicts.

  Args:
    predicate: The function to determine whether an element should be kept.
        Receives one argument for every structure that is provided.
    *structures: One of more nested structures.
    flatten: Whether to flatten the resulting structure into a tuple. Keys of
        dictionaries will be discarded.

  Returns:
    Nested structure.
  """
  # Named keyword arguments are not allowed after *args in Python 2.
  flatten = kwargs.pop('flatten', False)
  assert not kwargs, 'filter() got unexpected keyword arguments.'

  def impl(predicate, *structures):
    if len(structures) == 0:  # pylint: disable=len-as-condition
      return structures
    if all(isinstance(s, (tuple, list)) for s in structures):
      if len(set(len(x) for x in structures)) > 1:
        raise ValueError('Cannot merge tuples or lists of different length.')
      # Only wrap in tuples if more than one structure provided.
      if len(structures) > 1:
        filtered = (impl(predicate, *x) for x in _builtin_zip(*structures))
      else:
        filtered = (impl(predicate, x) for x in structures[0])
      # Remove empty containers and construct result structure.
      if hasattr(structures[0], '_fields'):  # namedtuple
        filtered = (x if x != () else None for x in filtered)
        return type(structures[0])(*filtered)
      else:  # tuple, list
        filtered = (
            x for x in filtered if not isinstance(x, (tuple, list, dict)) or x)
        return type(structures[0])(filtered)
    if all(isinstance(s, dict) for s in structures):
      if len(set(frozenset(x.keys()) for x in structures)) > 1:
        raise ValueError('Cannot merge dicts with different keys.')
      # Only wrap in tuples if more than one structure provided.
      if len(structures) > 1:
        filtered = {
            k: impl(predicate, *(s[k] for s in structures))
            for k in structures[0]}
      else:
        filtered = {k: impl(predicate, v) for k, v in structures[0].items()}
      # Remove empty containers and construct result structure.
      filtered = {
          k: v for k, v in filtered.items()
          if not isinstance(v, (tuple, list, dict)) or v}
      return type(structures[0])(filtered)
    if len(structures) > 1:
      return structures if predicate(*structures) else ()
    else:
      return structures[0] if predicate(structures[0]) else ()

  result = impl(predicate, *structures)
  if flatten:
    result = flatten_(result)
  return result
Select elements of a nested structure based on a predicate function.

  If multiple structures are provided as input, their structure must match and
  the function will be applied to corresponding groups of elements. The nested
  structure can consist of any combination of lists, tuples, and dicts.

  Args:
    predicate: The function to determine whether an element should be kept.
        Receives one argument for every structure that is provided.
    *structures: One of more nested structures.
    flatten: Whether to flatten the resulting structure into a tuple. Keys of
        dictionaries will be discarded.

  Returns:
    Nested structure.