diff --git a/NEWS.md b/NEWS.md index f00640ee..381d4f82 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # collapse 2.1.7 +* Fixed a bug in `fslice()` (grouped, `n = 1`, `with.ties = FALSE`) that caused R to crash with a fatal error when a group had only missing values in `order.by`. Thanks @chihyunkim for reporting (#867). + * The *collapse* article is now published in the Journal of Statistical Software: https://doi.org/10.18637/jss.v116.i01. This article is now the primary citation for academic use of *collapse*. It is also a great reference to quickly and thoroughly understand the package. `citation("collapse")` was also updated in this regard. The APA-style citation is: Krantz, S. (2026). **collapse**: Advanced and fast statistical computing and data transformation in R. *Journal of Statistical Software, 116*(1), 1–38. [https://doi.org/10.18637/jss.v116.i01](https://doi.org/10.18637/jss.v116.i01). diff --git a/src/small_helper.c b/src/small_helper.c index c70c22b4..575672b7 100644 --- a/src/small_helper.c +++ b/src/small_helper.c @@ -179,7 +179,7 @@ SEXP gwhich_first(SEXP x, SEXP g, SEXP target) { } case REALSXP: { const double *px = REAL_RO(x), *pt = REAL_RO(target)-1; - for(int i = 0; i != l; ++i) if(pres[pg[i]] == 0 && px[i] == pt[pg[i]]) pres[pg[i]] = i+1; + for(int i = 0; i != l; ++i) if(pres[pg[i]] == 0 && (px[i] == pt[pg[i]] || (ISNAN(px[i]) && ISNAN(pt[pg[i]])))) pres[pg[i]] = i+1; break; } case STRSXP: { diff --git a/tests/testthat/test-fslice.R b/tests/testthat/test-fslice.R index fcc55a5f..a1c8fba8 100644 --- a/tests/testthat/test-fslice.R +++ b/tests/testthat/test-fslice.R @@ -89,6 +89,26 @@ test_that("fslice works with grouping", { } }) +test_that("fslice does not crash with all-missing order.by in a group (#867)", { + data <- data.frame(group_var = c("a", "a", "b", "b", "c", "c"), + order_var = c(1, 2, 3, NA, NA, NA)) + + expect_equal( + fslice(data, "group_var", order.by = "order_var", how = "min", n = 1), + data.frame(group_var = c("a", "b", "c"), order_var = c(1, 3, NA)) + ) + + expect_equal( + fslice(data, "group_var", order.by = "order_var", how = "max", n = 1), + data.frame(group_var = c("a", "b", "c"), order_var = c(2, 3, NA)) + ) + + expect_equal( + fslice(data, "group_var", order.by = "order_var", how = "min", n = 1, with.ties = TRUE), + data.frame(group_var = c("a", "b", "c", "c"), order_var = c(1, 3, NA, NA)) + ) +}) + test_that("fslice works with ties", { N <- 1 # c(1, 5, 17) for (n in N) {