0

I have an ingredient dataset and each row is a list of ingredients that's separated by comma, for example:

Oats (24%) (Rolled, Bran), Coconut (13%) (Coconut , Preservative (220, 223)), Brown Sugar, Milk Solids, Golden Syrup (10%), Seeds (9%) (Sesame , Sunflower), Margarine (Vegetable Oil, Water, Salt, Emulsifiers (471, Soy Lecithin), Antioxidant (307)), Glucose, Milk Choc Compound (5%) (Sugar, Vegetable Oil, Milk Solids, Cocoa Powder, Emulsifiers (Soy Lecithin, 492), Natural Flavour), Natural Flavour

I want to parse the file to replace only commas within brackets with a semicolon. There can be any number of brackets and any number of commas within the brackets. The result should look like this:

Oats (24%) (Rolled;Bran), Coconut (13%) (Coconut ; Preservative (220; 223)), Brown Sugar, Milk Solids, Golden Syrup (10%), Seeds (9%) (Sesame ;Sunflower), Margarine (Vegetable Oil; Water; Salt; Emulsifiers (471; Soy Lecithin); Antioxidant (307)), Glucose, Milk Choc Compound (5%) (Sugar; Vegetable Oil; Milk Solids; Cocoa Powder; Emulsifiers (Soy Lecithin; 492); Natural Flavour), Natural Flavour

Can I get some help on regex that will solve the problem? Thank you in advance.

Catherine
  • 23
  • 4

2 Answers2

2

1) gsubfn This can be done without complex regular expressions using gsubfn. The regular expression consisting of a dot matches a single character. Then for each string in the input character vector the pre function initializes the counter k to 0 for then for each match fun is run with that character passed to it via the x argument. Within fun the counter k is incremented by 1 each time a ( is encountered and decremented by 1 each time a ) is encountered. If the counter is not zero and a comma is encountered a semicolon is returned to replace the comma; otherwise, the input character is returned. This is vectorized, i.e. it also works if the input s is a character vector in which each component should be processed separately.

library(gsubfn)

p <- proto(k = 0, 
  pre = function(this) this$k <- 0,
  fun = function(this, x) {
    if (x == "(") this$k <- k + 1
    if (x == ")") this$k <- k - 1
    if (k && x == ",") ";" else x
  })
gsubfn(".", p, s)

giving:

[1] "Oats (24%) (Rolled; Bran), Coconut (13%) (Coconut ; Preservative (220; 223)), Brown Sugar, Milk Solids, Golden Syrup (10%), Seeds (9%) (Sesame ; Sunflower), Margarine (Vegetable Oil; Water; Salt; Emulsifiers (471; Soy Lecithin); Antioxidant (307)), Glucose, Milk Choc Compound (5%) (Sugar; Vegetable Oil; Milk Solids; Cocoa Powder; Emulsifiers (Soy Lecithin; 492); Natural Flavour), Natural Flavour"

2) Base R A base R solution is to split the input into single characters giving a list of character vectors, L. Then for each component, chars, of L create a counter vector, k, the same length as chars which indicates the number of ( to that point minus the number of ) to that point. Then replace those commas corresponding to a nonzero k with semicolon and transform chars back to a single string. Like (1) this works on character vectors.

L <- strsplit(s, "")
sapply(L, function(chars) {
  k <- cumsum((chars == "(") - (chars == ")"))
  chars[k & chars == ","] <- ";"
  paste(chars, collapse = "")
})

Note

Input string s is the following.

s <- "Oats (24%) (Rolled, Bran), Coconut (13%) (Coconut , Preservative (220, 223)), Brown Sugar, Milk Solids, Golden Syrup (10%), Seeds (9%) (Sesame , Sunflower), Margarine (Vegetable Oil, Water, Salt, Emulsifiers (471, Soy Lecithin), Antioxidant (307)), Glucose, Milk Choc Compound (5%) (Sugar, Vegetable Oil, Milk Solids, Cocoa Powder, Emulsifiers (Soy Lecithin, 492), Natural Flavour), Natural Flavour"
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
1

You can use ?R like.

i <- gregexpr("\\(([^()]|(?R))*\\)", s, perl=TRUE)
regmatches(s, i)[[1]] <- gsub(",", ";", regmatches(s, i)[[1]])

s
#[1] "Oats (24%) (Rolled; Bran), Coconut (13%) (Coconut ; Preservative (220; 223)), Brown Sugar, Milk Solids, Golden Syrup (10%), Seeds (9%) (Sesame ; Sunflower), Margarine (Vegetable Oil; Water; Salt; Emulsifiers (471; Soy Lecithin); Antioxidant (307)), Glucose, Milk Choc Compound (5%) (Sugar; Vegetable Oil; Milk Solids; Cocoa Powder; Emulsifiers (Soy Lecithin; 492); Natural Flavour), Natural Flavour"

Where a(?R)z is a recursion which match one or more letters a followed by exactly the same number of letters z.

Data

s <- "Oats (24%) (Rolled, Bran), Coconut (13%) (Coconut , Preservative (220, 223)), Brown Sugar, Milk Solids, Golden Syrup (10%), Seeds (9%) (Sesame , Sunflower), Margarine (Vegetable Oil, Water, Salt, Emulsifiers (471, Soy Lecithin), Antioxidant (307)), Glucose, Milk Choc Compound (5%) (Sugar, Vegetable Oil, Milk Solids, Cocoa Powder, Emulsifiers (Soy Lecithin, 492), Natural Flavour), Natural Flavour"
GKi
  • 37,245
  • 2
  • 26
  • 48