diff --git a/NEWS.md b/NEWS.md index bcae1dc7..ef43c871 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,6 +11,9 @@ ## Features and fixes +* `map2()` and `pmap()` now recycle names of their first input if + needed (#783). + * `every()` now correctly propagates missing values using the same rules as `&&` (#751). Internally, it has become a wrapper around `&&`. This makes it consistent with `&&` and also with `some()` diff --git a/src/map.c b/src/map.c index e14b2af0..f8bda74c 100644 --- a/src/map.c +++ b/src/map.c @@ -7,14 +7,20 @@ #include "utils.h" void copy_names(SEXP from, SEXP to) { - if (Rf_length(from) != Rf_length(to)) - return; - SEXP names = Rf_getAttrib(from, R_NamesSymbol); - if (Rf_isNull(names)) + if (names == R_NilValue) { return; + } + + R_len_t n = Rf_length(to); + + if (Rf_length(names) != n) { + names = short_vec_recycle(names, n); + } + PROTECT(names); Rf_setAttrib(to, R_NamesSymbol, names); + UNPROTECT(1); } void check_vector(SEXP x, const char *name) { diff --git a/tests/testthat/test-map2.R b/tests/testthat/test-map2.R index 6e31db97..f82a7650 100644 --- a/tests/testthat/test-map2.R +++ b/tests/testthat/test-map2.R @@ -57,3 +57,14 @@ test_that("map2() with empty input copies names", { expect_identical(map2_chr(named_list, list(), identity), named(chr())) expect_identical(map2_raw(named_list, list(), identity), named(raw())) }) + +test_that("map2() and pmap() recycle names (#779)", { + expect_identical( + map2(c(a = 1), 1:2, ~ .x), + list(a = 1, a = 1) + ) + expect_identical( + pmap(list(c(a = 1), 1:2), ~ .x), + list(a = 1, a = 1) + ) +})