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$
5
  • $\begingroup$ Can you supply a sample expr? It's hard to provide help if other people don't have anything they can evaluate. $\endgroup$ Commented Jan 28, 2020 at 17:16
  • $\begingroup$ @J.M. sample expr added. $\endgroup$
    – user13892
    Commented Jan 28, 2020 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
    Commented Jan 28, 2020 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
    Commented Jan 28, 2020 at 18:13
  • $\begingroup$ Related: (25538) $\endgroup$
    – Mr.Wizard
    Commented Jan 30, 2020 at 3:51

3 Answers 3

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?

$\endgroup$
2
  • 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
    Commented Feb 10, 2020 at 16:08
  • $\begingroup$ @user13892 I am glad my answer was useful, and thanks for the Accept. $\endgroup$
    – Mr.Wizard
    Commented Feb 11, 2020 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

$\endgroup$
3
  • $\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
    Commented Jan 28, 2020 at 18:42
  • $\begingroup$ Also, can you provide a solution that works with Replace[#,{rules},All]& rather than ReplaceAll[#,{rules}]&. $\endgroup$
    – user13892
    Commented Jan 28, 2020 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
    Commented Jan 28, 2020 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.

$\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.