One of the challenges of coming to grips with Query
's on Association
's and Dataset
s is inculcating an internal programming model of how they act and combine. This, in turn, depends critically on whether Query
operators are ascending or descending either when acting alone or in concert as part of a function/query composition. Take the following example:
data = <|
"key" -> 1,
"keyA-Z" -> <|
"keyA" -> <|
"key1" -> A1,
"key2" -> A2,
"key3" -> A3|>,
"keyB" -> <|
"key1" -> B1,
"key2" -> B2,
"key3" -> B3|>
|>
|>;
Suppose we want to extract all elements at the "key2"
level - here {A2,B2}
. The query
Query["keyA-Z", All, "key2"]@data
(* <|"keyA" -> A2, "keyB" -> B2|> *)
does the job except for including the keys and while these can be readily peeled off ( Query["keyA-Z", All, "key2"]@data//Values
), for "composability purposes" it's nice to be able maintain the operator form of the query by aiming to bring Values
into the query itself.
Initial Attempts and Ascending/Descending casting:
At first pass
Query["keyA-Z", All /* Values, "key2"]@data
(*
<|
"keyA" -> <|"key1" -> A1, "key2" -> A2, "key3" -> A3|>,
"keyB" -> <|"key1" -> B1, "key2" -> B2, "key3" -> B3|>
|>[[Values]][[All, "key2"]]
*)
seems appropriate noting from the documentation:
When one or more descending operators are composed with one or more ascending operators (e.g.
desc /* asc
), the descending part will be applied, then subsequent operators will be applied to deeper levels, and lastly the ascending part will be applied to the result.
e.g.
Query["keyA-Z", All /* f, "key2"]@data
(* f[<|"keyA" -> A2, "keyB" -> B2|>] *)
but as observed in its output the catch here is that Values
is a descending operator (unlike the generic, ascending f
). We can try turning it into an ascending one by wrapping it in a Query
(subqueries tend to be ascending)
Query["keyA-Z", All /* Query[Values], "key2"]@data
(* <|"keyA" -> A2, "keyB" -> B2|>[[Values]] *)
but no beer. One way however, of turning Values
into a generic, ascending function is by reverting to its pure form
Query["keyA-Z", All /* (Values@# &), "key2"]@data
(* {A2, B2} *)
While this works and provides an idiom for casting descending operators into ascending ones, (and also bringing post-processing within queries), it's also rather inelegant. In fact things seem to have come full circle; in this question the value of eschewing pure functions in favour of operator forms was compellingly made in several answers but here their re-introduction seems necessary as part of an operator's specification?
So can this "keyless All
" Query
be constructed in non-pure terms? - more broadly, is there a way of systematically specifying Query
operators as either ascending or descending?