R4DSに書いてある話。
たとえばこういう要素があるとする。
l <- list( group1 = list( name = "group1", score = 10, user = list( loginname = "user1", emoji = ":sushi:" ) ), group2 = list( name = "group2", score = 11, user = list( loginname = "user2", emoji = ":llap:" ) ) )
ここで、name
を取り出すには"name"
を指定する。
l %>% map("name") #> $group1 #> [1] "group1" #> #> $group2 #> [1] "group2"
では、user
のloginname
を取り出すにはどうするのか。単純に考えれば、2回map()
すればいい。
l %>% map("user") %>% map("loginname") #> $group1 #> [1] "user1" #> #> $group2 #> [1] "user2"
でも、?map
を見るとこんなことが書かれている。
If character or integer vector, e.g.
"y"
, it is converted to an extractor function,function(x) x[["y"]]
. To index deeply into a nested list, use multiple values;c("x", "y")
is equivalent toz[["x"]][["y"]]
.
ということで、こんな風に書ける。
l %>% map(c("user", "loginname")) #> $group1 #> [1] "user1" #> #> $group2 #> [1] "user2"
ちなみに、こういうセマンティクスを採用したので、たとえばname
とscore
を取ってきたい、というときに
l %>% map(c("name", "score"))
とか書くことはできない。名前を並べていくとどんどん深い階層に潜っていくというもので、同じ階層にあるものを並列で取り出すというセマンティクスではない。でもそれは自明ではないので混乱するよね、というのがこのissueでの議論。
ちなみに、じゃあ今のところの正解は何かというと、
l %>% map(`[`, c("name", "score"))
らしい。
でもこれははっきり言って事故の元だと思う。要素名を直書きで指定している場合はいいけど、
x <- "name" l %>% map(x)
みたいなコードがあったときに、x
にc("user", "loginname")
とか、挙句の果てに1:10
とかが入っていてもうっかり動いてしまってエラーにならない。黙ってNULL
が返される。
x <- 1:10 l %>% map(x) #> [[1]] #> NULL #> #> [[2]] #> NULL
うーん。とりあえずissueを立ててみようかな…
追記(2016/09/03):
よくよく考えると、少なくとも文字列でインデックスを指定した時は、存在しない要素名でもエラーにならないのでした。
x <- list() x[["ninja"]] #> NULL
ネストしても大丈夫。
x[["ninja"]][["why"]][["ninja"]] #> NULL
というのは、[[
はNULL
に対しても使えるからです。
NULL[["wasshoi!"]] #> NULL
数値のインデックスを指定した時だけエラーになります。
x[[1]] #> Error in x[[1]] : subscript out of bounds
数値のインデックスでも、NULL
に対してやったときはエラーになりません。
NULL[[1]] #> NULL NULL[[1]][[2]] #> NULL
わけが分からないよ…
追記2(2016/09/03):
よく見るとうさぎさんのブログにちゃんと書いてました。そんなことできたのか。
補足 第二引数にベクトルを渡した場合の処理は、以下のように階層的な選択になる (
l[[c('a', 'b')]]
と一緒)。