diff --git a/NEWS.md b/NEWS.md index 6aae9977..442e2f66 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # stringr (development version) +* Add `sep` argument to `str_dup()` so that it is possible to repeat a string and + add a separator between every repeated value (@edward-burn, #564). * `str_*` now errors if `pattern` includes any `NA`s (@nash-delcamp-slp, #546). * `str_view()` now displays a message when called with a zero-length character vector (@LouisMPenrod, #497). diff --git a/R/dup.R b/R/dup.R index d90e62fb..3a82ffe0 100644 --- a/R/dup.R +++ b/R/dup.R @@ -5,14 +5,24 @@ #' #' @inheritParams str_detect #' @param times Number of times to duplicate each string. +#' @param sep String to insert between each duplicate. #' @return A character vector the same length as `string`/`times`. #' @export #' @examples #' fruit <- c("apple", "pear", "banana") #' str_dup(fruit, 2) +#' str_dup(fruit, 2, sep = " ") #' str_dup(fruit, 1:3) #' str_c("ba", str_dup("na", 0:5)) -str_dup <- function(string, times) { - vctrs::vec_size_common(string = string, times = times) - stri_dup(string, times) +str_dup <- function(string, times, sep = NULL) { + input <- vctrs::vec_recycle_common(string = string, times = times) + check_string(sep, allow_null = TRUE) + + if (is.null(sep)) { + stri_dup(input$string, input$times) + } else { + map_chr(seq_along(input$string), function(i) { + paste(rep(string[[i]], input$times[[i]]), collapse = sep) + }) + } } diff --git a/man/str_dup.Rd b/man/str_dup.Rd index fb722f2c..48a7867b 100644 --- a/man/str_dup.Rd +++ b/man/str_dup.Rd @@ -4,13 +4,15 @@ \alias{str_dup} \title{Duplicate a string} \usage{ -str_dup(string, times) +str_dup(string, times, sep = NULL) } \arguments{ \item{string}{Input vector. Either a character vector, or something coercible to one.} \item{times}{Number of times to duplicate each string.} + +\item{sep}{String to insert between each duplicate.} } \value{ A character vector the same length as \code{string}/\code{times}. @@ -22,6 +24,7 @@ A character vector the same length as \code{string}/\code{times}. \examples{ fruit <- c("apple", "pear", "banana") str_dup(fruit, 2) +str_dup(fruit, 2, sep = " ") str_dup(fruit, 1:3) str_c("ba", str_dup("na", 0:5)) } diff --git a/tests/testthat/_snaps/dup.md b/tests/testthat/_snaps/dup.md new file mode 100644 index 00000000..08a752c2 --- /dev/null +++ b/tests/testthat/_snaps/dup.md @@ -0,0 +1,13 @@ +# separator must be a single string + + Code + str_dup("a", 3, sep = 1) + Condition + Error in `str_dup()`: + ! `sep` must be a single string or `NULL`, not the number 1. + Code + str_dup("a", 3, sep = c("-", ";")) + Condition + Error in `str_dup()`: + ! `sep` must be a single string or `NULL`, not a character vector. + diff --git a/tests/testthat/test-dup.R b/tests/testthat/test-dup.R index 2872ae37..ca7ddbb9 100644 --- a/tests/testthat/test-dup.R +++ b/tests/testthat/test-dup.R @@ -13,3 +13,21 @@ test_that("0 duplicates equals empty string", { test_that("uses tidyverse recycling rules", { expect_error(str_dup(1:2, 1:3), class = "vctrs_error_incompatible_size") }) + +test_that("uses sep argument", { + expect_equal(str_dup("abc", 1, sep = "-"), "abc") + expect_equal(str_dup("abc", 2, sep = "-"), "abc-abc") + + expect_equal(str_dup(c("a", "b"), 2, sep = "-"), c("a-a", "b-b")) + expect_equal(str_dup(c("a", "b"), c(1, 2), sep = "-"), c("a", "b-b")) + + expect_equal(str_dup(character(), 1, sep = "-"), character()) + expect_equal(str_dup(character(), 2, sep = "-"), character()) +}) + +test_that("separator must be a single string", { + expect_snapshot(error = TRUE, { + str_dup("a", 3, sep = 1) + str_dup("a", 3, sep = c("-", ";")) + }) +})