たとえばこんなデータがあるとする。
{ "日付": { "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)
されてしまう。