Skip to content

Commit ebf3823

Browse files
authored
Support negative i in str_split_i() (#479)
1 parent f482fb0 commit ebf3823

File tree

4 files changed

+49
-10
lines changed

4 files changed

+49
-10
lines changed

R/split.R

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@
5151
#'
5252
#' # str_split_i extracts only a single piece from a string
5353
#' str_split_i(fruits, " and ", 1)
54-
#' str_split_i(fruits, " and ", 3)
54+
#' str_split_i(fruits, " and ", 4)
55+
#' # use a negative number to select from the end
56+
#' str_split_i(fruits, " and ", -1)
5557
str_split <- function(string, pattern, n = Inf, simplify = FALSE) {
5658
check_lengths(string, pattern)
5759
check_positive_integer(n)
@@ -89,12 +91,29 @@ str_split_fixed <- function(string, pattern, n) {
8991

9092
#' @export
9193
#' @rdname str_split
92-
#' @param i Element to return.
94+
#' @param i Element to return. Use a negative value to count from the
95+
#' right hand side.
9396
str_split_i <- function(string, pattern, i) {
94-
check_positive_integer(i)
97+
check_number_whole(i)
9598

96-
out <- str_split(string, pattern, simplify = NA, n = i + 1)
97-
out[, i]
99+
if (i > 0) {
100+
out <- str_split(string, pattern, simplify = NA, n = i + 1)
101+
out[, i]
102+
} else if (i < 0) {
103+
i <- abs(i)
104+
pieces <- str_split(string, pattern)
105+
last <- function(x) {
106+
n <- length(x)
107+
if (i > n) {
108+
NA_character_
109+
} else{
110+
x[[n + 1 - i]]
111+
}
112+
}
113+
map_chr(pieces, last)
114+
} else {
115+
cli::cli_abort("{.arg i} must not be 0.")
116+
}
98117
}
99118

100119
check_positive_integer <- function(x, arg = caller_arg(x), call = caller_env()) {

man/str_split.Rd

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/split.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,10 @@
3434
str_split_i("x", "x", 0)
3535
Condition
3636
Error in `str_split_i()`:
37-
! `i` must be a number larger than 1, not the number 0.
37+
! `i` must not be 0.
38+
Code
39+
str_split_i("x", "x", 0.5)
40+
Condition
41+
Error in `str_split_i()`:
42+
! `i` must be a whole number, not the number 0.5.
3843

tests/testthat/test-split.R

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,24 @@ test_that("str_split_fixed check its inputs", {
6363
expect_snapshot(str_split_fixed("x", "x", 0), error = TRUE)
6464
})
6565

66+
# str_split_i -------------------------------------------------------------
67+
68+
test_that("str_split_i can extract from LHS or RHS", {
69+
expect_equal(str_split_i(c("1-2-3", "4-5"), "-", 1), c("1", "4"))
70+
expect_equal(str_split_i(c("1-2-3", "4-5"), "-", -1), c("3", "5"))
71+
})
72+
6673
test_that("str_split_i returns NA for absent components", {
67-
expect_equal(str_split_i(c("a", "b-c"), "-", 1), c("a", "b"))
6874
expect_equal(str_split_i(c("a", "b-c"), "-", 2), c(NA, "c"))
6975
expect_equal(str_split_i(c("a", "b-c"), "-", 3), c(NA_character_, NA))
76+
77+
expect_equal(str_split_i(c("1-2-3", "4-5"), "-", -3), c("1", NA))
78+
expect_equal(str_split_i(c("1-2-3", "4-5"), "-", -4), c(NA_character_, NA))
7079
})
7180

7281
test_that("str_split_i check its inputs", {
73-
expect_snapshot(str_split_i("x", "x", 0), error = TRUE)
82+
expect_snapshot(error = TRUE, {
83+
str_split_i("x", "x", 0)
84+
str_split_i("x", "x", 0.5)
85+
})
7486
})

0 commit comments

Comments
 (0)