5

I have some assocations, containing a lot of data, lets say something like

<|"x"->{... big list ...},....|>

I would like to print these associations in a convenient and concise form, let's say:

x:[xMinValue,xMaxValue] (where xMinValue=Min[{..Big List...}]...)

For this purpose my idea was to use the Format command. Reading the official mma doc I have this example:

Format[f[x_?ListQ]] := "f[x], x:[" <> ToString[Min[x]] <> ", " <> ToString[Max[x]] <> "]"

For example:

f[Range[20]]]

f[x], x:[1, 20]

However, and this is my problem, I have no success with Association:

Format[<|"x"->x_?ListQ|>] := "x:[" <> ToString[Min[x]] <> ", " <> ToString[Max[x]] <> "]"

SetDelayed::write: Tag Association in MakeBoxes[Association[x_?ListQ],FormatType_] is Protected.

SetDelayed::write: Tag Association in Association[x_?ListQ] is Protected.

My question: how to define custom and pretty print of Association?

Kuba
  • 136,707
  • 13
  • 279
  • 740
Picaud Vincent
  • 2,463
  • 13
  • 20
  • 1
    First, Association have the propertiy Protected, so you have to Unprotect them first. Second, Associations are atomic. Hence, pattern matching within Associations does not work. – Henrik Schumacher Nov 23 '17 at 15:51

3 Answers3

3

Here's a closely related answer: 149668

MakeBoxes does not need to assign to Association in exchange for an overhead which usually is not a problem (see 39675)

Also, be extremely careful during pattern matching / calculations for formatting/typesetting. Your _?ListQ will evaluate content to confirm pattern even if the expression is somewhere inside Hold or something. Try Hold@<|"x" -> (Print[1]; {1, 2, 3})|>

Summing up, here's something to work with:

With[
  { safeNumListQ = Function[elem, NumericQ[Unevaluated[elem]], HoldAllComplete]
  }
, MakeBoxes[
    Association["x" -> x : {__?safeNumListQ}], fmt_
  ] := StringTemplate["\"x:[``, ``]\""] @@ MinMax[x]
]

In general you need to care about evaluation on MinMax stage too but here we already confirmed it is a numerical list and MinMax won't leak anything.

<|"x" -> {1, 2, 3}|>
Hold@<|"x" -> {1, 2, 3, Print[1]}|>
Hold@<|"x" -> {1, 2, 3}|>
Hold@<|"x" -> (Print[1]; {1, 2, 3})|>

enter image description here

Further reading:

Kuba
  • 136,707
  • 13
  • 279
  • 740
2

This could be a way to do it. Use it without any guarantees from my side.

Unprotect[Association];
format[x_List] := "<< Very long list that we do not want to see, honestly. >>";
format[x_] := x;
Association /: Format[a_Association] := 
 Row[{"\[LeftAssociation]", 
   Sequence @@ 
    Riffle[Rule @@@ Transpose[{Keys[a], format /@ Values[a]}], ", "], 
   "\[RightAssociation]"}]
Protect[Association];

Here some example to see the effect:

Association["Bli" -> 1, "Bla" -> Association["Blabla" -> {2, 3}], "Blubb" -> {4, 5}]
Henrik Schumacher
  • 106,770
  • 7
  • 179
  • 309
  • Great! That was the kind of solution I was looking for. Many thanks. I will just wait a little bit to see if another solution is proposed. – Picaud Vincent Nov 23 '17 at 16:17
1

Inspired by @HenrikSchumacher answer, here is my answer, that filters the Association type I want to print in a special way.

First I define this test, used to check association type:

MyAssocationQ[assoc_] := MatchQ[assoc, <|"x" -> _?ListQ|>]

For instance:

MyAssocationQ[<|"x" -> {-5, 2, 9}|>]

returns

True

however

MyAssocationQ[2017]
MyAssocationQ[<|"x" -> {-5, 2, 9},"y"->1|>]

return

False

False

Then, following Henrik approach, we have to Unprotect the Assocation symbol, and define

Unprotect[Association];

Association /: Format[assoc_Association /; MyAssocationQ[assoc]] := 
  "x:[" <> ToString[Min[assoc["x"]]] <> ", " <> 
  ToString[Max[assoc["x"]]] <> "]";

Protect[Association];

Then I get the expected behavior:

In[]= <|"x" -> {-5, 2, 9}|>

Out[]= x:[-5, 9]

and

In[]= <|"x" -> {-5, 2, 9}, "y" -> 5|>

Out[]= <|"x" -> {-5, 2, 9}, "y" -> 5|>

is unaffected and is the usual behavior.

Picaud Vincent
  • 2,463
  • 13
  • 20