たとえばこんなデータがあるとする。
{
"日付": {
"type": "date",
"value": "2015-11-11"
},
"何の日": {
"type": "char",
"value": "ポッキーの日"
},
"俺の中での重要度": {
"type": "int",
"value": "3"
}
}
これを読むときに、愚直にやるとたぶんこんな感じ。
library(jsonlite) library(purrr) j <- fromJSON("test.json") map(j, ~ switch(.$type, date = as.Date(.$value), char = as.character(.$value), int = as.integer(.$value))) #> $`日付` #> [1] "2015-11-11" #> #> $何の日 #> [1] "ポッキーの日" #> #> $俺の中での重要度 #> [1] 3
これを、S3 generic functionを使う手もあることにふと気づいた。
まず、typeの値をclassとして設定する。
j_w_class <- map(j, ~ structure(.$value, class = .$type)) j_w_class #> $`日付` #> [1] "2015-11-11" #> attr(,"class") #> [1] "date" #> #> $何の日 #> [1] "ポッキーの日" #> attr(,"class") #> [1] "char" #> #> $俺の中での重要度 #> [1] "3" #> attr(,"class") #> [1] "int"
classごとのgeneric functionを用意する。
parse_j <- function(x) UseMethod("parse_j") parse_j.default <- function(x) as.character(x) parse_j.date <- function(x) as.Date(x) parse_j.char <- function(x) as.character(x) parse_j.int <- function(x) as.integer(x)
これを適用する。
map(j_w_class, parse_j) #> $`日付` #> [1] "2015-11-11 UTC" #> #> $何の日 #> [1] "ポッキーの日" #> #> $俺の中での重要度 #> [1] 3
これはたぶん、Domain specific languages · Advanced R.の例のHTMLみたいに、ネストする構造がある場合に見通しがよくなる気がする。switch()でも再帰すればいいだけではあるけど。
はまりどころ
これをパッケージ化するときにちょっとはまったのでメモ。(このへんはS3 generic functionというよりroxygen2の仕様の問題なのかも)
#' @export f <- function(x) UseMethod("f") f.default <- function(x) as.character(x)
とかやると、あたりまえだけどf.default()はエクスポートされない。ggplot2:::print.ggplot()とかを見て、S3のメソッドって見えないからエクスポートしないものだと思い込んでたけど違った。
あと、
#' @export f.default <- as.character
とかいう感じで別のS3 generic functionを代入するのもできない。上の例だと、S3Method(f.default)じゃなくてexport(f.default)されてしまう。