Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I have a big list which in principle can be described as this more compact list :

list1 = {{e, {{a, 0.1}, {b, 0.3}, {c, 0.5}, {d, 0.7}}, 
    f, {{a, 0.2}, {b, 0.4}, {c, 0.6}, {d, 0.8}}, g, h, i}};

I am interested in rewriting to the following :

{{e, {"abcd", 0.1, 0.3, 0.5, 0.7}, f, {"abcd", 0.2, 0.4, 0.6, 0.8}, g,
   h, i}}

which works well when applying this simple rule:

list1 /. {{a, x1_}, {b, x2_}, {c, x3_}, {d, x4_}} -> {"abcd", x1, x2, 
   x3, x4}

However when the affected sublists {a,x1} to {d,x4} are not on the same depth, I have difficulties constructing a rule that works. So for instance:

list2 = {{e, {a, 0.1}, {b, 0.3}, {c, 0.5}, {d, 0.7}, 
   f, {{a, 0.2}, {b, 0.4}, {c, 0.6}, {d, 0.8}}, g, h, i}}

does not give the desired result.

So my questions are: 1. Is there a way to ReplaceAll independent of depth? 2. Is there a more elegant method that does the job?

share|improve this question

3 Answers 3

up vote 4 down vote accepted

Level really has nothing to do with the problem you are experiencing; ReplaceAll already works equivalently at all levels, following a depth-first preorder traversal.

Instead your problem is that the section of the expression that you wish to replace is not self-contained in list2. In list1 it is complete:

{{a, 0.1}, {b, 0.3}, {c, 0.5}, {d, 0.7}}

In list2 it is a subsequence of a larger expression:

{e, {a, 0.1}, {b, 0.3}, {c, 0.5}, {d, 0.7}, f, . . .}

This is simply not the same structure and it is not matched by your pattern. I believe the simplest workaround is to add a BlankNullSequence to be beginning and end of your pattern:

rule =
  {p___, {a, x1_}, {b, x2_}, {c, x3_}, {d, x4_}, q___} :>
    {p, {"abcd", x1, x2, x3, x4}, q};

(Note the use of RuleDelayed which is necessary to localize pattern names and which should have been used in your original rule as well.)

And then use ReplaceRepeated to catch all cases:

list2 //. rule
{{e, {"abcd", 0.1, 0.3, 0.5, 0.7}, f, {{"abcd", 0.2, 0.4, 0.6, 0.8}}, g, h, i}}

This is however terribly inefficient as many different alignments must be tried and the expression is repeatedly scanned. I am thinking about a better alternative that is sufficiently flexible.


The only simple optimization that comes to mind is replace ReplaceRepeated with a manual application of /. on only the parts of the expression that have not already been scanned. This should greatly improve performance on long lists as it will prevent the entire expression from being rescanned for every replacement.

rule2 = {p___, {a, x1_}, {b, x2_}, {c, x3_}, {d, x4_}, q___} :> 
   Join[{p, {"abcd", x1, x2, x3, x4}}, {q} /. rule2];

Note the self-referential use of rule2; it is important to keep this Symbol defined for the replacement to work. Now:

list2 /. rule2
{{e, {"abcd", 0.1, 0.3, 0.5, 0.7}, f, {{"abcd", 0.2, 0.4, 0.6, 0.8}}, g, h, i}}
share|improve this answer
    
Nice! I tried this approach myself but now see my mistake... I forgot to add a letter to the BlankNullSequence ___ . Darn, I was so close :) –  MathLind Aug 14 at 17:48
1  
@MathLind This question/operation comes up so often I really think Mathematica needs a simpler syntax for it. I wish that this would work: list2 /. PatternSequence[{a, x1_}, {b, x2_}, {c, x3_}, {d, x4_}] :> {"abcd", x1, x2, x3, x4} (It of course doesn't.) –  Mr.Wizard Aug 14 at 17:52
1  
Hear hear. But alas, it has good reasons not to work ... –  Teake Nutma Aug 14 at 18:51
    
@TeakeNutma I missed that question; thanks for the link! –  Mr.Wizard Aug 14 at 18:53
    
@TeakeNutma What Tali wrote: ... when it really could be O(n), if we had better ways of describing and using sequence-based patterns. What we really need is one or two functions with the same semantics as the String* functions ... is exactly what I am talking about, though on reflection I agree that PatternSequence likely plays no role in that. I have actually converted expressions into strings simply for the sequence handling, which seems indicative of a hole in the language at present. –  Mr.Wizard Aug 14 at 18:57
list2 = {{e, {a, 0.1}, {b, 0.3}, {c, 0.5}, {d, 0.7}, f, {{a, 0.2}, {b, 0.4}, {c, 0.6}, {d, 0.8}}, g, h, i}};

list2 /.
  {e, head__, f , tail__} :> {e, {head}, f, tail} /.
    {{a, x1_}, {b, x2_}, {c, x3_}, {d, x4_}} :> {"abcd", x1, x2, x3, x4}

{{e, {a, 0.1}, {b, 0.3}, {c, 0.5}, {d, 0.7}, f, {"abcd", 0.2, 0.4, 0.6, 0.8}, g, h, i}}

share|improve this answer
    
This works very well eldo. Let me metabolize your code a while. BTW how do you make your output yellow like above. –  MathLind Aug 14 at 17:28
    
@MathLind Thanks, but see my comment to Öska's answer :) –  eldo Aug 14 at 17:30
    
@MathLind The yellow background is because he used the blockquote button in the editor instead of the code sample button. –  Pickett Aug 14 at 17:46
    
@Pickett Thanks, I will apply this in the future. –  MathLind Aug 14 at 17:50

I tried another alternative using Replace with {0, Infinity} levelspec and it was surprisingly faster than ReplaceAll in the other answer.

replace = Replace[#, {p___, {a, x1_}, {b, x2_}, {c, x3_}, {d, x4_}, 
      q___} -> {p, {"abcd", x1, x2, x3, x4}, q}, {0, Infinity}] &;
replace[list2]
(* {{e, {"abcd", 0.1, 0.3, 0.5, 0.7}, f, {{"abcd", 0.2, 0.4, 0.6, 0.8}}, 
  g, h, i}} *)

Comparing methods

list = Nest[
  Append[List /@ #, 
    Transpose@{{a, b, c, d}, RandomReal[1, 4]}~Prepend~
     RandomChoice[CharacterRange["a", "z"]]] &, {}, 10]

Mathematica graphics

Equal @@ (#@list & /@ {# //. rule &, # /. rule2 &, replace[#] &})
(* True *)

Timings

Module[{rule, rule2, replace, listCompare, timings},
 rule = {p___, {a, x1_}, {b, x2_}, {c, x3_}, {d, x4_}, 
    q___} :> {p, {"abcd", x1, x2, x3, x4}, q};
 rule2 = {p___, {a, x1_}, {b, x2_}, {c, x3_}, {d, x4_}, q___} :> 
   Join[{p, {"abcd", x1, x2, x3, x4}}, {q} /. rule2];
 replace = 
  Replace[#, {p___, {a, x1_}, {b, x2_}, {c, x3_}, {d, x4_}, 
      q___} -> {p, {"abcd", x1, x2, x3, x4}, q}, {0, Infinity}] &;
 listCompare = {# //. rule &, # /. rule2 &, replace[#] &};
 timings[n_Integer] := 
  With[{list = 
     Nest[Append[List /@ #, 
        Transpose@{{a, b, c, d}, RandomReal[1, 4]}~Prepend~
         RandomChoice[CharacterRange["a", "z"]]] &, {}, n]},
   {n, First@AbsoluteTiming@#@list} & /@ listCompare];
 timings /@ Range[100, 1000, 100] // Transpose // 
  ListLinePlot[#, PlotLegends -> {"rule", "rule2", "replace"}, 
    AxesLabel -> {"Levels", "AbsoluteTiming"}] &]

Mathematica graphics

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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