Dario Lacan
Dario Lacan

Reputation: 1140

extract the difference ("relative complement") between two strings in r

I can't find a way to do this...

raw_string <- "\"+001\", la bonne surprise de M. Jenn M. Ayache http://goo.gl/3EXxy6 via @MYTF1News"

clean_string <- "+001, la bonne surprise de Jenn Ayache"

desired_string <- "\"\"M. M. http://goo.gl/3EXxy6 via @MYTF1News"

I am not sure about how to call this transformation. I would say "difference" (as in set theory, opposed to "union" and "intersection"). A better name could be "relative complement" (http://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement).

My desired string has only and all the characters missing from the clean_string, in the good order, once for every time they appear, including spaces, punctuation and everything.

The best I managed to do isn't good enough:

> a <- paste(Reduce(setdiff, strsplit(c(raw_string, clean_string), split = " ")), collapse = " ")
> a
[1] "\"+001\", M. http://goo.gl/3EXxy6 via @MYTF1News"

Upvotes: 6

Views: 579

Answers (3)

cdeterman
cdeterman

Reputation: 19960

Here is a little more concise way using sub which requires you to account for symbols.

str_relative_complement <- function(raw_string, clean_string){
    words <- strsplit(clean_string, "")[[1]]
    cur_str <- raw_string
    for(i in words){
        cur_str <- sub(ifelse(grepl("[[:punct:]]", i), paste0("\\", i), i), "", cur_str)
    }
    return(cur_str)
}

raw_string <- '\"+001\", la bonne surprise de M. Jenn M. Ayache http://goo.gl/3EXxy6 via @MYTF1News'
clean_string <- "+001, la bonne surprise de Jenn Ayache"

str_relative_complement(raw_string, clean_string)
[1] "\"\"M. M.  http://goo.gl/3EXxy6 via @MYTF1News"

Upvotes: 1

Roland
Roland

Reputation: 132706

I would use a loop, too:

x <- strsplit(raw_string, "")[[1]]
y <- strsplit(clean_string, "")[[1]]

res <- character(length(x))

j <- 1

for(i in seq_along(x)) {
  if (j > length(y)) {
    res[i:length(x)] <- x[i:length(x)]
    break
  }
  if (x[i] != y[j]) {
    res[i] <- x[i]
  } else {
    j <- j + 1
  }
}

paste(res, collapse = "")
#[1] "\"\"M. M.  http://goo.gl/3EXxy6 via @MYTF1News"

Note the extra space in comparison to your expected result. I think you simply missed it.

If this is too slow, it should be easy to implement with Rcpp.

Upvotes: 3

konvas
konvas

Reputation: 14346

I don't know if there is an implemented function for this in one of the string manipulation packages (I haven't come across it). This is an implementation which (I think) works

raw_string <- "\"+001\", la bonne surprise de M. Jenn M. Ayache http://goo.gl/3EXxy6 via @MYTF1News"
clean_string <- "+001, la bonne surprise de Jenn Ayache"
raw <- strsplit(raw_string, "")[[1]]
clean <- strsplit(clean_string, "")[[1]]
dif <- vector("list")
j <- 1
while(length(clean) > 0) {
    i <- match(clean[1], raw)
    if (i > 1) {
        dif[[j]] <- raw[seq_len(i - 1)]
        j <- j + 1
    }
    clean <- clean[-1]
    raw <- raw[-seq_len(i)]
}
dif[[j]] <- raw
paste(unlist(dif), collapse = "")
#[1] "\"\"M. M.  http://goo.gl/3EXxy6 via @MYTF1News"

Upvotes: 1

Related Questions