7
$\begingroup$

Related to this question, can functions be mapped to named Keys in ragged lists of Associations?

ds = {<|"a" -> 1, "b" -> 2|>, <|"b" -> 3, "c" -> 4|>}

This fails:

ds // Query[All, {"a" -> f}]
{<|"a" -> f[1], "b" -> 2|>, Missing["Failed"]}

As does this:

ds // MapAt[f, {All, "a"}]

MapAt::partw: Part {All,a} of {<|a->1,b->2|>,<|b->3,c->4|>} does not exist.

One strategy might be to normalize the list by inserting Missing values using KeyUnion

ds // KeyUnion // Query[All, {"a" -> f}]
{<|"a" -> f[1], "b" -> 2,    "c" -> Missing["KeyAbsent", "c"]|>, 
 <|"a"->     f[Missing["KeyAbsent", "a"]], "b" -> 3, "c" -> 4|>}

But in this case is there a way to ignore Missing? Perhaps using Position? I did not see a specific MissingBehavior option that works here.

EDIT

As pointed out here, FilterRules works in some cases but it's limited.

This is interesting as that's a v6 function intended for use with Options rather than the v10 Associations.

Here is a the operator version:

filterRules[expr_][as_Association] := Query[FilterRules[expr, as]][as];

Example:

{<|"a" -> 1, "b" -> 2|>, <|"a" -> 3|>, <|"b" -> 4, "c" -> 5|>} // 
 Query[All, filterRules@{"a" -> f, "b" -> g, "c" -> h}]
{<|"a" -> f[1], "b" -> g[2]|>, <|"a" -> f[3]|>, <|"b" -> g[4], "c" -> h[5]|>}

However, there are some fails when applied to Associations. If the Rules don't mention at least one Key in a given Association, it returns an empty Association - this is at odds with the Query[{key-> f}] semantics.

Dataset[{<|"a" -> 1, "b" -> 2|>, <|"b" -> 3, "c" -> 4|>}][All, 
 filterRules@{"a" -> f}]
{<|"a" -> f[1], "b" -> 2|>, <||>}

Also, doesn't seem to work on symbolic Keys:

Dataset[{<|"a" -> 1, False -> 2|>, <|False -> 3, "c" -> 4|>}][All, 
 filterRules@{"a" -> f, Key[False] -> g}]
{<|"a" -> f[1], False -> 2|>, <||>}

And - warning - crashes the kernel with composite Keys:

Dataset[{<|"a" -> 1, {"b1", "b2"} -> 2|>, <|{"b1", "b2"} -> 3, 
     "c" -> 4|>}][All, 
  filterRules@{"a" -> f, Key[{"b1", "b2"}] -> g}] // n
$\endgroup$
5
$\begingroup$
ds = {
   <|"a" -> 1, "b" -> 2|>, 
   <|"b" -> 3, "c" -> <|"a" -> 1, "g" -> 2|>|>
}

Not perfect but I'd probably use it:

ds // Query[
  All, 
  Replace[s : KeyValuePattern["a" -> _] :> MapAt[f, s, "a"]]
]
{
  <|"a" -> f[1], "b" -> 2|>, 
  <|"b" -> 3,  "c" -> <|"a" -> 1, "g" -> 2|>|>
}

Or

... :> <|s, "a" -> f @ s @ "a"|>

or

Query[
   All, 
   If[KeyExistsQ["a"][#], MapAt[f, "a"][#], #] &
]
$\endgroup$
4
$\begingroup$

Rather hackish but perhaps still of use:

Quiet@*MapAt[f, "a"] /@ ds /. _MapAt[x_] :> x
{<|"a" -> f[1], "b" -> 2|>, <|"b" -> 3, "c" -> 4|>}

Responding to your update something like this might work:

filterRules[expr_][as_Association] := 
  as // Query[
     FilterRules[expr, Join[#, Key /@ #] & @ Keys @ as]
       // Replace[{} -> Identity]
   ]
$\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.