Skip to content

Instantly share code, notes, and snippets.

@jrosell
Last active July 31, 2025 10:22
Show Gist options
  • Save jrosell/49917cbfd4b1e7e29591af9ec35d6713 to your computer and use it in GitHub Desktop.
Save jrosell/49917cbfd4b1e7e29591af9ec35d6713 to your computer and use it in GitHub Desktop.
lapply and unlist are common base R operations chained together. That's the typical flatmap operation
# Refrence: https://www.biobits.be/biofunctor/2025/07/30/what-s-r-vector-victor/
# Laboratory experiments are often performed in 96-well plastic plates, with 8 rows (labeled A-H) and 12 columns (labeled 1-12). Each microwell is a separate micro-experiment (labeled A1-H12).
rows <- LETTERS[1:8]
columns <- 1:12 |> sprintf(fmt = "%02i")
# Recycled vectors is not what we want here.
paste0(rows, columns)
#> [1] "A01" "B02" "C03" "D04" "E05" "F06" "G07" "H08" "A09" "B10" "C11" "D12"
paste01 <- \(x, y) { stopifnot(length(x) == 1L); paste0(x, y)}
flatmap <- function(X, FUN, ..., USE.NAMES = TRUE) {
unlist(lapply(X, FUN, ...), recursive = FALSE, use.names = USE.NAMES)
}
map_vec <- function(X, FUN, ...) {
Vectorize(function(x) FUN(x, ...))(X) |>
as.vector()
}
results <- bench::mark(
expand_grid = {
grid <- expand.grid(Column = columns, Row = rows)
expand_grid_res <- paste0(grid$Row, grid$Column)
expand_grid_res
},
rep = {
rows_rep <- rep(rows, each = length(columns))
columns_rep <- rep(columns, times = length(rows))
rep_res <- paste0(rows_rep, columns_rep)
rep_res
},
paste01_lapply_unlist = {
paste01_lapply_unlist_res <- rows |>
lapply(paste01, columns) |>
unlist(recursive = FALSE)
paste01_lapply_unlist_res
},
flatmap_paste01 = {
flatmap_paste01_res <- flatmap(rows, paste01, columns)
flatmap_paste01_res
},
flatmap_paste0 = {
flatmap_paste0_res <- flatmap(rows, paste0, columns)
flatmap_paste0_res
},
vectorize_paste0 = {
vectorize_paste0_res <-
Vectorize(\(x) paste0(x, columns))(rows) |> as.vector()
vectorize_paste0_res
},
vectorize_paste01 = {
vectorize_paste01_res <-
Vectorize(\(x) paste01(x, columns))(rows) |> as.vector()
vectorize_paste01_res
},
map_vec_paste0 = {
map_vec_paste0_res <-
rows |> map_vec(paste0, columns)
map_vec_paste0_res
},
map_vec_paste01 = {
map_vec_paste01_res <-
rows |> map_vec(paste01, columns)
map_vec_paste01_res
}
)
results[,c("expression", "median", "mem_alloc")]
#> # A tibble: 9 × 3
#> expression median mem_alloc
#> <bch:expr> <bch:tm> <bch:byt>
#> 1 expand_grid 43.1µs 63.59KB
#> 2 rep 10.3µs 2.39KB
#> 3 paste01_lapply_unlist 31.1µs 4.01MB
#> 4 flatmap_paste01 31.3µs 816B
#> 5 flatmap_paste0 18µs 23.41KB
#> 6 vectorize_paste0 55.4µs 71.24KB
#> 7 vectorize_paste01 68.7µs 7.34KB
#> 8 map_vec_paste0 55µs 2.39KB
#> 9 map_vec_paste01 69.5µs 35.95KB
rep_res
#> [1] "A01" "A02" "A03" "A04" "A05" "A06" "A07" "A08" "A09" "A10" "A11" "A12"
#> [13] "B01" "B02" "B03" "B04" "B05" "B06" "B07" "B08" "B09" "B10" "B11" "B12"
#> [25] "C01" "C02" "C03" "C04" "C05" "C06" "C07" "C08" "C09" "C10" "C11" "C12"
#> [37] "D01" "D02" "D03" "D04" "D05" "D06" "D07" "D08" "D09" "D10" "D11" "D12"
#> [49] "E01" "E02" "E03" "E04" "E05" "E06" "E07" "E08" "E09" "E10" "E11" "E12"
#> [61] "F01" "F02" "F03" "F04" "F05" "F06" "F07" "F08" "F09" "F10" "F11" "F12"
#> [73] "G01" "G02" "G03" "G04" "G05" "G06" "G07" "G08" "G09" "G10" "G11" "G12"
#> [85] "H01" "H02" "H03" "H04" "H05" "H06" "H07" "H08" "H09" "H10" "H11" "H12"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment