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.

Given a List of rectangular nx3 arrays of data, each of the form {...,{x,y,z},...}, (the ellipses are not Mathematica pattern syntax, just for illustration). Some members of this list may be - by coincidence - square, ie 3x3.

What's the appropriate method to match each {x,y,z} row regardless of whether n is 3 or not? For example, the following expression,

With[{n = 4},
 Graphics@ Table[Random[], {i, 1, n}, {j, 1, 3}] /. {a_, b_, c_} :> 
   Line[{{1, a}, {2, b}, {3, c}}]
 ]

Only works as intended for n!=3. When n==3, then "a" matches the 1st row, "b" matches the 2nd, "c" the 3rd row.

I've tried Repeated, ie, {{a_,b_,c_}..}, but that results in a different error that I've not analyzed.

This is a simplification of a more involved database problem. Any suggestions?

share|improve this question
add comment

3 Answers

up vote 10 down vote accepted

Generally, a rule-within-a-rule approach can be often used to solve this kind of problems. The exterior rule serves as a "filter" for the inner one, where the most work is done. I discussed it in some detail here. Another nice example which shows the power of this technique is here, which is, in some sense, a generalization of the answer below.

For the pure pattern-matching purposes, this should work:

{{_, _, _} ..}

You can optionally use _Real or _?NumericQ instead of just _. It is another question how you do a destructuring on them, will need more work, e.g.

t = Partition[Range[12], 3]
t /. p : {{_, _, _} ..} :> 
      Replace[p, {a_, b_, c_} :> Line[{{1, a}, {2, b}, {3, c}}], 1]
 {Line[{1, 1}, {2, 2}, {3, 3}], Line[{1, 4}, {2, 5}, {3, 6}], 
     Line[{1, 7}, {2, 8}, {3, 9}], Line[{1, 10}, {2, 11}, {3, 12}]}

As you can see, a rule within a rule is used, which is a generally useful technique and often yields elegant solutions.

share|improve this answer
    
Thanks, that works even with Partition[Range[9],3], which was the problematic case, though I've not tested it on my original analysis code. Curious, why doesn't p : {{a_, b_, c_} ..} work (ie, named pattern and named subpatterns, that would obviate the rule within rule portion) –  alancalvitti Jun 7 '12 at 21:11
3  
@alancalvitti It does not work because such a pattern is overly restrictive: all first, second and third elements would have to be the same, so only things like {{1,2,3},{1,2,3},...} would match. –  Leonid Shifrin Jun 7 '12 at 22:07
    
Thank you, understood. –  alancalvitti Jun 7 '12 at 22:31
add comment

First a note: you can condense Table[Random[], {i, 1, n}, {j, 1, 3}] to RandomReal[1, {n, 3}].

Rojo gives the method I prefer, but uses a different Level specification. This is fine if you know the level at which your expressions to replace appear, and they all appear at the same level. However, if you need behavior that is similar to ReplaceAll this will not work. For example, suppose you have this expression and intend to manipulate the natural numbers:

x = {{1, 2, 3}, {-1, {4, 5, 6}, -2}, {{-3, {{7, 8, 9}, -4, -5}, -6}}};

To catch all cases you can replace at level {-2}: one level up from the bottom, because the pattern is one level deep:

Replace[x, {a_, b_, c_} :> a + b + c, {-2}]
{6, {-1, 15, -2}, {{-3, {24, -4, -5}, -6}}}

For your example and using /. you can successfully restrict the pattern in several ways:

n = 3;

Graphics@RandomReal[1, {n, 3}] /.
 {a_Real, b_, c_} :> Line[{{1, a}, {2, b}, {3, c}}]

Graphics@RandomReal[1, {n, 3}] /.
 {a_?AtomQ, b_, c_} :> Line[{{1, a}, {2, b}, {3, c}}]

Graphics@RandomReal[1, {n, 3}] /.
 {a : Except[_List], b_, c_} :> Line[{{1, a}, {2, b}, {3, c}}]

Graphics@RandomReal[1, {n, 3}] /.
 Except[{__List}, {a_, b_, c_}] :> Line[{{1, a}, {2, b}, {3, c}}]

Any of these may be desirable depending on the circumstance. Do not consider any of these "magic" but understand why they work.

share|improve this answer
    
+1. Nice set of solutions. I think the first (Replace[x, {a_, b_, c_} :> a + b + c, {-2}]) is the most efficient and elegant one. –  Alexey Popkov Jun 8 '12 at 8:09
    
@Alexey Thanks! I appreciate that coming from you. By the way I miss your contributions; post more often if you have the time. –  Mr.Wizard Jun 8 '12 at 8:12
    
@Rojo ah yes; that's why I specifically mentioned "because the pattern is one level deep" -- depending on circumstances this method would need to be combined with one of the restricting methods shown in the second part of my answer, which is why I included these. –  Mr.Wizard Jun 8 '12 at 13:57
    
+1, nice addition –  Rojo Jun 8 '12 at 14:05
add comment

@Leonid gave you a very powerful and general approach.

I want to add that for your particular case where all the expressions you want to replace are at a certain level, you can use Replace instead of /.(ReplaceAll).

With[{n = 3}, Replace[
  Graphics@Table[Random[], {i, 1, n}, {j, 1, 3}], {a_, b_, c_} :> 
   Line[{{1, a}, {2, b}, {3, c}}], {2}]]

This works because it only tries the replacements in the matrix rows (level 2)

share|improve this answer
add comment

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.