13
$\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$

2 Answers 2

9
$\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)

$\endgroup$
10
$\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}]|> *)
$\endgroup$

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.