Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to split a string from right-to-left, like Python's rsplit()?

Suppose a vector:

xx.1 <- c("zz_ZZ_uu_d", "II_OO_d")

I want to get a new vector splitted from right most and only split once. The expected results would be:

c("zz_ZZ_uu", "d", "II_OO", "d").

It would be like python's rsplit() function. My current idea is to reverse the string, and split the with str_split() in stringr.

Any better solutions?

update
Here is my solution returning n splits, depending on stringr and stringi. It would be nice that someone provides a version with base functions.

rsplit <- function (x, s, n) {
  cc1 <- unlist(stringr::str_split(stringi::stri_reverse(x), s, n))
  cc2 <- rev(purrr::map_chr(cc1, stringi::stri_reverse))
  return(cc2)
}
like image 424
ccshao Avatar asked Dec 25 '22 16:12

ccshao


2 Answers

Negative lookahead:

unlist(strsplit(xx.1, "_(?!.*_)", perl = TRUE))
# [1] "zz_ZZ_uu" "d"        "II_OO"    "d"     

Where a(?!b) says to find such an a which is not followed by a b. In this case .*_ means that no matter how far (.*) there should not be any more _'s.

However, it seems to be not that easy to generalise this idea. First, note that it can be rewritten as positive lookahead with _(?=[^_]*$) (find _ followed by anything but _, here $ signifies the end of a string). Then a not very elegant generalisation would be

rsplit <- function(x, s, n) {
  p <- paste0("[^", s, "]*")
  rx <- paste0(s, "(?=", paste(rep(paste0(p, s), n - 1), collapse = ""), p, "$)")
  unlist(strsplit(x, rx, perl = TRUE))
}

rsplit(vec, "_", 1)
# [1] "a_b_c_d_e_f" "g"           "a"           "b"          
rsplit(vec, "_", 3)
# [1] "a_b_c_d" "e_f_g"   "a_b"    

where e.g. in case n=3 this function uses _(?=[^_]*_[^_]*_[^_]*$).

like image 173
Julius Vainora Avatar answered Dec 28 '22 10:12

Julius Vainora


Another two. In both I use "(.*)_(.*)" as the pattern to capture both parts of the string. Remember that * is greedy so the first (.*) will match as many characters as it can.

Here I use regexec to capture where your susbtrings start and end, and regmatches to reconstruct them:

unlist(lapply(regmatches(xx.1, regexec("(.*)_(.*)", xx.1)),
              tail, -1))

And this one is a little less academic but easy to understand:

unlist(strsplit(sub("(.*)_(.*)", "\\1@@@\\2", xx.1), "@@@"))
like image 22
flodel Avatar answered Dec 28 '22 09:12

flodel