11
$\begingroup$

One of the challenges of coming to grips with Query's on Association's and Datasets 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?

$\endgroup$

1 Answer 1

2
$\begingroup$

While I do not believe that this addresses all points in your question I note that the specific query you attempted works in this form:

Query["keyA-Z", Values /* All, "key2"] @ data
{A2, B2}

The same with Dataset:

Dataset[data]["keyA-Z", Values /* All, "key2"]

enter image description here

Based on this I would have thought that Composition also would work but it does not:

Query["keyA-Z", All @* Values, "key2"] @ data

Values::invrl: The argument Values[Slice[key2]] is not a valid Association or a list of rules. >>

$\endgroup$
6
  • $\begingroup$ Actually Query["keyA-Z", Values, "key2"]@data also gives {A2, B2} suggesting All wasn't needed after all? $\endgroup$ Jul 12, 2015 at 7:38
  • $\begingroup$ @Ronald I hadn't thought of that but I think it makes sense. That answers your actual question (as I understand it) even less however? $\endgroup$
    – Mr.Wizard
    Jul 12, 2015 at 7:42
  • $\begingroup$ Yes it means that the example motivates somewhat less the, still relevant and broader casting question. It does mean though that my intuition about using All for listing "leaves" in nested Association's needed updating. The right construct here is Values - suppose we wanted all the leaves {A1, A2, A3, B1, B2, B3} then not All at all but instead Query["keyA-Z", Values /* Flatten, Values]@data $\endgroup$ Jul 12, 2015 at 9:02
  • $\begingroup$ forgot to accept, but as part of doing so now, for quick reference for others, perhaps you could include the "obvious" solution given in the first comment as the first line in your answer? $\endgroup$ Jan 22, 2016 at 11:00
  • $\begingroup$ @RonaldMonson, "suggesting All wasn't needed after all?" - that's right, All hardly if ever needed in query composition. $\endgroup$ Jul 31, 2017 at 17:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.