20

Consider

{x->1}

which has FullForm

List[Rule[x, 1]]

and, therefore,

MatchQ[{x->1}, List[___Rule]]

produces True.

Now consider

<|x->1|>

which has FullForm

Association[Rule[x, 1]]

but

MatchQ[<|x->1|>, Association[___Rule]]

produces False

In fact,

MatchQ[<|x->1|>, Association[___]]

produces False. Any ideas? (I'm trying to make some MathLink xxxxxxxx WSTP functions for serializing C structs as Associations, and this lacuna is messing me up).

RunnyKine
  • 33,088
  • 3
  • 109
  • 176
Reb.Cabin
  • 8,661
  • 1
  • 34
  • 62
  • MatchQ[ Normal @ <|x->1|>, Normal @ Association[ x -> _ ] or MatchQ[ Normal @ <|x->1|>, List[ ___ Rule] ] will work though. – gwr Sep 26 '15 at 14:16

2 Answers2

19

Association is atomic:

<|x -> 1|> // AtomQ
True

Therefore standard pattern matching inside the structure will not work.

You can still match on the implicit head using:

MatchQ[<|x -> 1|>, _Association]
True

There is also AssociationQ:

<|x -> 1|> // AssociationQ
True
MatchQ[<|x -> 1|>, _?AssociationQ]
True

I used the term atomic in a general way meaning an object that does not conform to the standard expression syntax and traversal rules. Taliesin Beynon explains why this is conflating two different concepts in this chat transcript.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 11
    It just so happens that Association is currently AtomQ, though I've argued strongly against that fact internally, and I've got SW's say-so that we'll change that eventually. But that doesn't have all that much to do with pattern matching not working inside associations: we all agree it should, it's just hard to do efficiently and we couldn't deliver it for 10. So, to sum up: Association will not be AtomQ forever, and it will support pattern matching eventually. There's a correlation here, not a causation. – Taliesin Beynon Jul 22 '14 at 05:33
  • 2
    @Taliesin Thanks for the clarification, however I'm afraid I don't understand it. If Assocation objects were standard Mathematica expressions then pattern matching would work by default, would it not? I realize pattern matching could be overloaded for an Atomic object as I believe it is for Complex, but I still think there is causation here. Am I mistaken? – Mr.Wizard Jul 22 '14 at 05:40
  • I can see that pattern-matching over the contents of an Association would be difficult. Thanks for the info! – Reb.Cabin Jul 22 '14 at 12:18
  • A possibly similar problem arises, for instance, in deciding whether the two JSON objects {a: 1, b: 2} and {b: 2, a: 1} are equal; the problem explodes with size, requiring either some arbitrary sort on the keys or considering $n!$ possibilities (modulo nesting). – Reb.Cabin Jul 22 '14 at 12:25
  • @Reb.Cabin, since the sorting is arbitrary can't the data be normalized internally? eg like FullForm[x + 1] == FullForm[1 + x] = Plus[1,x] – alancalvitti Jul 22 '14 at 14:07
  • @alancalvitti -- yes, it can, but, so far as I know, there is no standard for this in JSON and developers often write really bad code for testing equality of JSON objects. Mathematica has a standard (if obscure) order for terms in expressions and freely reorders things for you in evaluation. But an Association is probably reordered in Hash order or Trie order or something else altogether and would have to be reordered for pattern matching. That's my guess -- just a guess -- why Taliesen said it's difficult to be efficient. – Reb.Cabin Jul 22 '14 at 14:10
11

In Mathematica 10.4, Association can now be used in pattern matching. Here is the result of the OP's example:

MatchQ[<|x -> 1|>, Association[___Rule]]

True

and

MatchQ[<|x->1|>, Association[___]]

True

There's now also KeyValuePattern which is a pattern object specifically to match elements of an Association or list of rules. Here are some examples:

<|a -> 1, b -> 2, c -> 3|> /. KeyValuePattern[x_ -> 1] :> x

a

Cases[{
  <|"PartOfSpeech" -> "Noun", "Number" -> "Singular"|>,
  <|"PartOfSpeech" -> "Verb"|>},
 KeyValuePattern[{x : "PartOfSpeech" -> y : "Noun"}] :> 
  Association[x -> y]]

{<|"PartOfSpeech" -> "Noun"|>}

RunnyKine
  • 33,088
  • 3
  • 109
  • 176