14
$\begingroup$

This question is a follow up from my previous question.

Query has a nice syntax that allows it to apply functions on association based on specific Keys.

assoc=<|"a"->{1,3,2,9,4},"b"->{6,1,8},"c"->{3,2,8,9,8},"d"->{5},"e"->{5,3}|>;
Query[{"b"->f,"a"->g}]@assoc

<|"a"->g[{1,3,2,9,4}],"b"->f[{6,1,8}],"c"->{3,2,8,9,8},"d"->{5},"e"->{5,3}|>

Or even chain functions (right to left) on specific Keys.

Query[{"b"->f,"a"->g,"b"->h}]@assoc

<|"a"->g[{1,3,2,9,4}],"b"->f[h[{6,1,8}]],"c"->{3,2,8,9,8},"d"->{5},"e"->{5,3}|>

Note to use this functionality of Query on Numeric Keys one has to be explicit otherwise it has the interpretation of Part index for Integers:

assoc=<|1->{1,3,2,9,4},"b"->{6,1,8},2.1->{3,2,8,9,8},2->{5},"e"->{5,3}|>;
Query[{Key[2.1]->f,"b"->g,Key[1]->h,2->k}]@assoc

<|1->h[{1,3,2,9,4}],"b"->g[k[{6,1,8}]],2.1`->f[{3,2,8,9,8}],2->{5},"e"->{5,3}|>

Now my question is there a way to make this work for general patterns:

Query[{"a"|"b"|"c"->f,_->g,Except["b"]->h}]@assoc
$\endgroup$
10
$\begingroup$

Since I am not aware of any built-ins, here is another custom solution:

MapAtPattern[p_ -> f_, expr_] := MapIndexed[
  If[MatchQ[#2[[1]], Key@p],
    f@#,
    #
    ] &,
  expr
  ]
MapAtPattern[spec_][expr_] := MapAtPattern[spec, expr]
MapAtPattern[rules_List, 
  expr_] := (RightComposition @@ MapAtPattern /@ rules)@expr

It supports essentially the syntax from your question:

assoc = <|1 -> {1, 3, 2, 9, 4}, "b" -> {6, 1, 8}, 
   2.1 -> {3, 2, 8, 9, 8}, 2 -> {5}, "e" -> {5, 3}|>;

MapAtPattern[{"a" | "b" | "c" -> f, _ -> g, Except["b"] -> h}]@assoc
(* <|
     1 -> h[g[{1, 3, 2, 9, 4}]], 
     "b" -> g[f[{6, 1, 8}]], 
     2.1 -> h[g[{3, 2, 8, 9, 8}]],
     2 -> h[g[{5}]],
     "e" -> h[g[{5, 3}]]
   |> *)

It can also be used as part of a more complex Query:

Query[MapAtPattern@{"a" | "b" | "c" -> f, _ -> g, Except["b"] -> h}]@assoc
(* same output *)

As you can see, every matching rule is applied, not only the first (in contrast to the answer from @WReach)

| improve this answer | |
$\endgroup$
11
$\begingroup$

There is no built-in facility to do this kind of transformation, but we can define our own query operator like so:

patterned[template:PatternSequence[_Rule...]][data_Association] :=
  Module[{x}
  , data //
    Query[Replace[Keys[data], Append[x:# :> x -> #2 & @@@ {template}, _ -> Nothing], {1}]]
  ]

So then:

assoc // Query[patterned["a"|"d" -> f]]

(* <| "a" -> f[{1,3,2,9,4}], "b" -> {6,1,8},
      "c" -> {3,2,8,9,8}, "d" -> f[{5}], "e" -> {5,3} |> *)


assoc // Query[patterned["a"|"c" -> f, Except["b"] -> h, _ -> g]]

(* <| "a" -> f[{1, 3, 2, 9, 4}], "b" -> g[{6, 1, 8}], 
      "c" -> f[{3, 2, 8, 9, 8}], "d" -> h[{5}], "e" -> h[{5, 3}]|> *)
| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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