Reputation: 47340
I would like a function that will find function instances in a string, extract the raw parameters,and replace them with placeholders. Unfortunately my regex skills don't lead me far...
I would like the folowing behavior:
extract_fun("max(7*xy,b=z)+maximum+max(j)",fun="max")
# $modified_string
# [1] "{F[[1]]}+maximum+{F[[2]]}"
#
# $params
# $params[[1]]
# [1] "7*xy" "b=z"
#
# $params[[2]]
# [1] "j"
Edit:
A more complicated use case:
extract_fun("max(7*xy,b=min(1,3))+maximum+max(j)",fun="max")
# $modified_string
# [1] "{F[[1]]}+maximum+{F[[2]]}"
#
# $params
# $params[[1]]
# [1] "7*xy" "b=min(1,3)"
#
# $params[[2]]
# [1] "j"
Upvotes: 0
Views: 98
Reputation: 19736
Here is something to get you started:
Your function should have two arguments:
fun = "max"
string = "max(7*xy,b=z)+maximum+max(j)"
the regex captures anything in (
, )
, preceded by fun
and it is lazy ?
regex = paste0(fun, "\\((.*?)\\)")
regex
#output
"max\\((.*?)\\)"
matcher = stringr::str_match_all(string, regex)
matcher = do.call(rbind, matcher)
matcher
#output
[,1] [,2]
[1,] "(7*xy,b=z)" "7*xy,b=z"
[2,] "(j)" "j"
#extract arguments from captured groups in matcher[,2]
params = strsplit(matcher[,2], " {0,}, {0,}" ) #, with possible white spaces before and after
#output
[[1]]
[1] "7*xy" "b=z"
[[2]]
[1] "j"
#generate a modified_string
Fs = 1:nrow(matcher)
replacer = paste0("{F[[", Fs, "]]}")
regex2 = paste(matcher[,1])
out = string
for (i in 1:length(replacer)){
out= gsub(regex2[i], replacer[i], out , fixed = TRUE)
}
out
#output
"{F[[1]]}+maximum+{F[[2]]}"
EDIT: here is what I have so far on the updated question:
My idea is to isolate the part of the string with the function of interest and than manipulate this part only.
string = "max(7*xy,b=min(1,3))+maximum+max(j)"
Split string just behind max(
fun = "max"
regex_for_split = paste0("(?<=.)(?=", fun, "\\()")
fun_char = nchar(fun)
spliter_begin = unlist(strsplit(string, regex_for_split, perl = TRUE))
Locate opening and ending parentheses
opening = stringr::str_locate_all(spliter_begin, "\\(")
ending = stringr::str_locate_all(spliter_begin, "\\)")
clean it up a bit
opening = lapply(opening, function(x){
return((x[,1]))
})
ending = lapply(ending, function(x){
return((x[,1]))
})
find at what position is the amount of ending parentheses equal to the amount of opened parentheses. We are interested in the first match.
out = list()
for (i in 1: length(ending)){
end = ending[[i]]
open = opening[[i]]
sumer = vector()
for(z in end){
sumi= sum(open < z) == sum(end<=z)
sumer = c(sumer, sumi)
}
out[[i]] = sumer
}
spliter_end = purrr::map2(ending, out, function(x, y){
return(x[y])
})
isolate the sub string
fun_isolate = purrr::map2(as.list(spliter_begin), spliter_end, function(x,y){
substr(x, start = fun_char+2, stop = y[1]-1)
})
fun_isolate
#output
[[1]]
[1] "7*xy,b=min(1,3)"
[[2]]
[1] "j"
lets try with a meaner example
string2 = "max(7*xy,b=min(1,3),z=sum(x*y)),mean(x+y)+maximum+max(j)"
#copy above code with `string2` instead of `string`
fun_isolate
[[1]]
[1] "7*xy,b=min(1,3),z=sum(x*y)"
[[2]]
[1] "j"
Or even tougher:
string3 = "max(7*xy,b=min(1,3, head(z)),z=sum(x*y+mean(x+y))),mean(x+y)+maximum+max(j)"
#output
[[1]]
[1] "7*xy,b=min(1,3, head(z)),z=sum(x*y+mean(x+y))"
[[2]]
[1] "j"
Now its just a matter of splitting at ,
not surrounded by (
)
.
#locate strings in parenthesis
locate_exclude = stringr::str_locate_all(unlist(fun_isolate), "\\(.*?\\)")
#locate all comas
locate_comma = stringr::str_locate_all(unlist(fun_isolate), ",")
#leave the difference
splt_locate = purrr::map2(locate_exclude, locate_comma, function(x, y){
if(length(x)==0) x = matrix(data=c(0,0), nrow=1)
offbounds = vector()
for (i in 1 : nrow(x)){
z = x[i,1]:x[i,2]
offbounds = c(offbounds, z)
}
comas = y[,1]
return(comas[!comas%in%offbounds])
})
#function to split string at indexes
split_str_by_index <- function(target, index) {
index <- sort(index)
substr(rep(target, length(index) + 1),
start = c(1, index),
stop = c(index -1, nchar(target)))
}
close_but_not_yet = purrr::map2(fun_isolate, splt_locate, function(x, y){
split_str_by_index(x, y)
})
close_but_not_yet
#output
[[1]]
[1] "7*xy" ",b=min(1,3, head(z))" ",z=sum(x*y+mean(x+y))"
[[2]]
[1] "j"
And just remove ,
on the start of the string if there is one. Example:
lapply(close_but_not_yet , function(x) gsub("^, {0,}", "",x))
#output
[[1]]
[1] "7*xy" "b=min(1,3, head(z))" "z=sum(x*y+mean(x+y))"
[[2]]
[1] "j"
it won't work if the same function is called within itself like:
"max(7*xy,b=min(1,3),z=max(x*y)),mean(x+y)+maximum+max(j)"
but even that could be manageable if you exclude all from (
)
prior the first strsplit like in the ,
example.
tested with:
"max(7*xy,b=min(1,3, head(z)),z=sum(x*y+mean(x+y))),mean(x+y)+maximum+max(j)"
"max(7*xy,b=min(1,3, head(z)),z=sum(x*y+mean(x+y))),mean(x+y)+maximum+max(j*z+sum(a*b^sum(z)), drop = 72)"
"max(7*xy,b=min(1,3, head(z)),z=sum(x*y, mean(x+y))),mean(x+y)+maximum+max(j*z+sum(a*b^sum(z)), drop = 72)"
Upvotes: 2