7
$\begingroup$

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?

$\endgroup$

3 Answers 3

10
$\begingroup$

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.

$\endgroup$
2
  • $\begingroup$ 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) $\endgroup$ Jun 7, 2012 at 21:11
  • 3
    $\begingroup$ @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. $\endgroup$ Jun 7, 2012 at 22:07
6
$\begingroup$

@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)

$\endgroup$
6
$\begingroup$

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.

$\endgroup$
3
  • $\begingroup$ +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. $\endgroup$ Jun 8, 2012 at 8:09
  • $\begingroup$ @Alexey Thanks! I appreciate that coming from you. By the way I miss your contributions; post more often if you have the time. $\endgroup$
    – Mr.Wizard
    Jun 8, 2012 at 8:12
  • $\begingroup$ @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. $\endgroup$
    – Mr.Wizard
    Jun 8, 2012 at 13:57

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.