9
$\begingroup$

I have an expr which has List at various levels and I want to display them nicely as a column inside a frame but my problem is that the search for List goes inside things like DateObject to ruin their structure.

Replace[
    expr,
    x_List/;!MatchQ[...[...x...],DateObject[x,__]]:>Framed[Column@x]
    All
]

So how should I define such a Rule?


Sample expr:

{1->a,2->b,3->c,4->{5->d,6->e,7->Today},8->Yesterday,9->{10->f,11->{12->Tomorrow,13->g}}}

Current output:

Framed[Column[{1 -> a, 2 -> b, 3 -> c, 4 -> Framed[Column[{5 -> d, 6 -> e, 7 -> DateObject[Framed[Column[{2020, 1, 28}]], "Day", CalendarType -> "Gregorian", DateFormat -> Automatic]}]], 
       8 -> DateObject[Framed[Column[{2020, 1, 27}]], "Day", CalendarType -> "Gregorian", DateFormat -> Automatic], 
       9 -> Framed[Column[{10 -> f, 11 -> Framed[Column[{12 -> DateObject[Framed[Column[{2020, 1, 29}]], "Day", CalendarType -> "Gregorian", DateFormat -> Automatic], 13 -> g}]]}]]}]]

Desired output:

Framed[Column[{1 -> a, 2 -> b, 3 -> c, 4 -> Framed[Column[{5 -> d, 6 -> e, 7 -> DateObject[{2020, 1, 28}, "Day", CalendarType -> "Gregorian", DateFormat -> Automatic]}]], 
       8 -> DateObject[{2020, 1, 27}, "Day", CalendarType -> "Gregorian", DateFormat -> Automatic], 
       9 -> Framed[Column[{10 -> f, 11 -> Framed[Column[{12 -> DateObject[{2020, 1, 29}, "Day", CalendarType -> "Gregorian", DateFormat -> Automatic], 13 -> g}]]}]]}]]
$\endgroup$
  • $\begingroup$ Can you supply a sample expr? It's hard to provide help if other people don't have anything they can evaluate. $\endgroup$ – J. M.'s discontentment Jan 28 at 17:16
  • $\begingroup$ @J.M. sample expr added. $\endgroup$ – user13892 Jan 28 at 17:38
  • $\begingroup$ I'm a little bit confused about what exactly you want the output to look like. Are you trying to extract every expression that has List as a Head and then put those in a column? Or are you trying to extract just the rules at level 1, or what? Can you give a (rough) example of your desired output? $\endgroup$ – march Jan 28 at 17:59
  • $\begingroup$ @march There is no extraction, it is a replacement problem at any level. See the current vs desired output. $\endgroup$ – user13892 Jan 28 at 18:13
  • $\begingroup$ Related: (25538) $\endgroup$ – Mr.Wizard Jan 30 at 3:51
4
$\begingroup$

Another option is Developer`ReplaceAllUnheld.

expr = {1 -> a, 2 -> b, 3 -> c, 4 -> {5 -> d, 6 -> e, 7 -> Today}, 8 -> Yesterday, 
   9 -> {10 -> f, 11 -> {12 -> Tomorrow, 13 -> g}}};

Block[{DateObject},
 Attributes[DateObject] = HoldAll; 
 Developer`ReplaceAllUnheld[expr, List -> Framed@*Column@*List]
]

enter image description here

Also see What is the potential usage of ReplaceAllUnheld in Developer Utilities Package?

| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ Thank you all for the wonderful answers and I have upvoted all of them but after careful consideration, I am accepting this one since it is very easy to extend and I don't have to define additional rules. In my application, I had to skip multiple Heads and just increasing Block variables and a line like SetAttributes[{DateObject,Entity,Style,Missing},HoldAll]; seemed far convenient than defining separate rules. $\endgroup$ – user13892 Feb 10 at 16:08
  • $\begingroup$ @user13892 I am glad my answer was useful, and thanks for the Accept. $\endgroup$ – Mr.Wizard Feb 11 at 2:28
15
$\begingroup$

You can use the fact that once an object is replaced, it doesn't get replaced by any further rules. So, just add a rule that replaces a DateObject with itself:

expr = {1 -> a, 2 -> b, 3 -> c, 4 -> {5 -> d, 6 -> e, 7 -> Today}, 8 -> Yesterday, 9 -> {10 -> f, 11 -> {12 -> Tomorrow, 13 -> g}}}l
expr /. {a_DateObject :> a, List -> Framed@*Column@*List}

enter image description here

| improve this answer | |
$\endgroup$
  • $\begingroup$ Please explain the reason for changing x_List:>Framed[Column@x] to List->Framed@*Column@*List and why the former doesn't work in your trick? $\endgroup$ – user13892 Jan 28 at 18:42
  • $\begingroup$ Also, can you provide a solution that works with Replace[#,{rules},All]& rather than ReplaceAll[#,{rules}]&. $\endgroup$ – user13892 Jan 28 at 18:43
  • 2
    $\begingroup$ @user13892 you won't be able to get this to work well with Replace[#, {rules}, All]& since that traverses the expression tree from the lowest level upwards. You really do need ReplaceAll. And List->Framed@*Column@*List is replacing the Head List with (in essence) the function (Framed@Column@List[##]&) which just means that you never need to bind x $\endgroup$ – b3m2a1 Jan 28 at 19:26
5
$\begingroup$

We can get the desired result using Replace in several ways:

1. We can temporarily change the behavior of Framed inside expressions with head DateObject (using Block and TagSetDelayed):

Block[{Framed}, 
 Framed /: DateObject[Framed[a_], b___] := DateObject[First @ a, b]; 
 Replace[expr, a_List :> Framed[Column @ a], All]]

enter image description here

Alternatively,

Block[{Framed}, 
 Framed /: DateObject[Framed[Column[a_]], b___] := DateObject[a, b];
 Replace[expr, a_List :> Framed[Column@a], All]]

same picture

2. Alternatively, we can use two replacement rules where the second rule undoes the replacements inside DateObjects effected by the first rule:

Replace[expr, {a_List :> Framed[Column@a], d_DateObject :> (d /. Framed -> First)}, All]

enter image description here

Alternatively,

Replace[expr, {List -> Framed@*Column@*List, 
  DateObject -> (DateObject[##] /. Framed -> First &)}, All, Heads -> True]

same picture

Note the need to use the option Heads -> True in the second approach.

| improve this answer | |
$\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.