Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for the idea of flatten_if() and squash() in list_flatten() #1064

Open
DavisVaughan opened this issue Mar 27, 2023 · 1 comment
Open
Labels
feature a feature request or enhancement flatten 🌎

Comments

@DavisVaughan
Copy link
Member

DavisVaughan commented Mar 27, 2023

I added this to dplyr, but it probably belongs in purrr and would make for an easier transition path for the deprecated rlang::flatten_if() and rlang::squash() functions

See r-lib/rlang#1576 and tidyverse/dplyr#6759

#' @param x A list
#' @param fn An optional function of 1 argument to be applied to each list
#'   element of `x`. This allows you to further refine what elements should be
#'   flattened. `fn` should return a single `TRUE` or `FALSE`.
#' @param recursive Should `list_flatten()` be applied recursively? If `TRUE`,
#'   it will continue to apply `list_flatten()` as long as at least one element
#'   of `x` was flattened in the previous iteration.
#' @noRd
list_flatten <- function(x, ..., fn = NULL, recursive = FALSE) {
  check_dots_empty0(...)

  obj_check_list(x)
  x <- unclass(x)

  loc <- map_lgl(x, obj_is_list)

  if (!is_null(fn)) {
    loc[loc] <- map_lgl(x[loc], fn)
  }

  not_loc <- !loc

  names <- names(x)
  if (!is_null(names)) {
    # Always prefer inner names, even if inner elements are actually unnamed.
    # This is what `rlang::flatten_if()` did, with a warning. We could also
    # use `name_spec` and `name_repair` for a more complete solution.
    names[loc] <- ""
    names(x) <- names
  }

  x[loc] <- map(x[loc], unclass)
  x[not_loc] <- map(x[not_loc], list)

  out <- list_unchop(x, ptype = list())

  if (recursive && any(loc)) {
    out <- list_flatten(out, fn = fn, recursive = TRUE)
  }

  out
}
@hadley hadley added feature a feature request or enhancement flatten 🌎 labels Jul 26, 2023
@olivroy
Copy link

olivroy commented May 17, 2024

If I understand correctly, this would solve the following problem right?

I have a list of this format. (duplicate names, but similar structure)

l1 <- list(x = c(el1 = 1), x = c(el2 = 2, el3 = 3), y = c(el1 = 1))
l1
$x
el1 
  1 

$x
el2 el3 
  2   3 
$y
  el1
  4

I'd like to apply a purrr transformation to change it to

list(x = c(el1 = 1, el2 = 2, el3 = 3), y = c(el1 = 1))

I tried using list_flatten(), but it insists on keeping l1 structure unchanged.

I may have gotten lost, but couldn't find an example that does this.

I was able to almost solve this with base R unlist()

which ends up giving

unlist(l1)
x.el1 x.el2 x.el3 y.el1
    1     2     3   1

but I don't really trust how unlist() handles pretty much anything in a surprising way..

unlist() is also not mentioned in the vignette https://purrr.tidyverse.org/articles/base.html (which doesn't reflect the 1.0 API exactly) i.e. mentions map_df*() functions, and doesn't mention newly introduced functions.

What would be a close equivalent of purrr vs unlist() ?

list_c() almost works, but it would be great if it had a name_spec argument.

purrr::list_c(l1)
el1 el2 el3 el1 
  1   2   3   2 

Loses the names

Edit: from #998, vctrs::list_unchop() seems to have a way to deal with names, maybe a xref could be inserted somewhere...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature a feature request or enhancement flatten 🌎
Projects
None yet
Development

No branches or pull requests

3 participants