Multiple versions of same function

Hola, novice package builder in need of advice.

I need to parse a batch of spreadsheets that have the same data, but in five or six distinct formats. I can handle this when the processing steps are small:

obj1 <- list(a = 'A', b = NULL)
obj2 <- list(a = NULL, b = 'B')

parser <- function(obj) {
  if(is.null(obj$a))
    return(obj$b)
  
  if(is.null(obj$b))
    return(obj$a)
}

parser(obj1)
#> [1] "A"
parser(obj2)
#> [1] "B"

Created on 2019-10-30 by the reprex package (v0.3.0)

But anything involving more than a few lines quickly becomes unwieldy.

Question: How can I have multiple versions of the same function, without creating some monolith full of if/else or switch statements?

If this were an API, I'd write: parser/v1/obj and parser/v2/obj and have versions of code with different addresses.

Stretch goal - how I can I refactor and organise this code, so that it's clear which code maps to which format?

Many thanks!

1 Like

Good question. An off-the-top-of-my-head answer is to write your function with argument parameters that can vary with the pattern. I can't tell without reproducible example, called a reprex whether you need to go further into a full-fledged parser, but that's another option.

Here are two ideas. First, I avoid branching with if() and try to write functions that handle the differences more generically. This function returns all non-Null elements of the list.

obj1 <- list(a = 'A', b = NULL)
obj2 <- list(a = NULL, b = 'B')
parser <- function(obj) {
  NotNull <- sapply(obj, function(x) !is.null(x))
  unlist(obj[NotNull])
}
parser(obj1)
#>   a 
#> "A"
parser(obj2)
#>   b 
#> "B"

Created on 2019-10-29 by the reprex package (v0.3.0.9000)

Another approach, about which I know just enough to be dangerous, is to use the S3 object system to write functions for different classes of objects and assign classes to your data according to the format. The book Advanced R explains how this works.

#Define a generic function
parser2 <- function(obj) {
  UseMethod("parser2", obj)
}
#provide functions for different classes of objects
parser2.first <- function(obj) {
  return(obj$a)
}

parser2.second <- function(obj) {
  return(obj$b)
}

#Make objects and give them classes corresponding to the functions above
obj3 <- list(a = 'A', b = NULL)
class(obj3) <- "first"
obj4 <- list(a = NULL, b = 'B')
class(obj4) <- "second"

parser2(obj3)
#> [1] "A"
parser2(obj4)
#> [1] "B"

Created on 2019-10-29 by the reprex package (v0.3.0.9000)

4 Likes