14
$\begingroup$

I'm surprised that I can't find this question already, so perhaps I'm missing something obvious. I would like to take a list of associations and remove any with value "red". I ran into the following problem:

{<|8 -> 7|>, <|"a" -> "red", 6 -> 3|>} /. <|"a" -> "red", s___|> :> Nothing

outputs

{<|8 -> 7|>, <|"a" -> "red", 6 -> 3|>}

rather than my expected

{<|8 -> 7|>}

However,

{<|8 -> 7|>, <|"a" -> "red", 6 -> 3|>} /. <|"a" -> "red", 6 -> 3|> :> Nothing

works as expected, to give {<|8 -> 7|>}. It's as if Association is applying Verbatim or something.

Why does the replacement fail here? Of course, the corresponding version using lists instead of associations works.

$\endgroup$
1
  • 2
    $\begingroup$ Funnily enough: MatchQ[Association["a" -> "red", 6 -> 3], Association["a" -> "red", __]] returns False. (For comparison, replacing Association[] with List[] gives True.) $\endgroup$ Commented Dec 12, 2015 at 10:26

2 Answers 2

12
$\begingroup$

Associations are funny beasts -- neither fish nor fowl. Sometimes they are treated as atomic expressions and sometime they are not. For pattern matching purposes their atomic nature seems to rule. Consider this far more elementary case.

a = <|"a" -> 2|>;
AtomQ @ a

True

Any expression can be used as a pattern and always matches itself.

MatchQ[a, <|"a" -> 2|>]

True

But other than this trivial case, no pattern based the form of a matches a. All of

MatchQ[a, <|_ -> 2|>]
MatchQ[a, <|"a" -> _|>]
MatchQ[a, <|_ -> _|>]
MatchQ[a, <|_|>]

give False. This is the behavior to be expected of atoms, so to modify an association with ReplaceAll, it looks like you are going to have use Normal.

Association[Normal[a] /. "a" -> "b"]

<|"b" -> 2|>

$\endgroup$
3
  • $\begingroup$ A very nice explanation, +1 $\endgroup$
    – eldo
    Commented Dec 12, 2015 at 16:03
  • 1
    $\begingroup$ The tedious-looking workaround: {<|8 -> 7|>, <|"a" -> "red", 6 -> 3|>} /. a_Association /; KeyMemberQ[a, "a"] && a["a"] === "red" :> Nothing. The last example can also be done with KeyMap[]: KeyMap[# /. "a" -> "b" &, <|"a" -> 2|>]. $\endgroup$ Commented Dec 12, 2015 at 16:10
  • 3
    $\begingroup$ I'll add that pattern-matching of this kind for Association is in active development internally. $\endgroup$
    – Stefan R
    Commented Dec 14, 2015 at 19:49
7
$\begingroup$

As of 10.4, there is now KeyValuePattern:

{<|8 -> 7|>, <|"a" -> "red", 6 -> 3|>} /. KeyValuePattern["a" -> "red"] :> Nothing

returns

{<|8 -> 7|>}

(The code reads a little weirdly - going by names alone, an Association shouldn't match a KeyValuePattern in my mind, because an Association isn't a key/value pair - but the intention is clear.)

$\endgroup$

Your Answer

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

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