# Does anyone know a way to use patterns in Query?

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->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


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

MapAtPattern[p_ -> f_, expr_] := MapIndexed[
If[MatchQ[#2[], 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)

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}]|> *)
`