Simplifying nested If statements

In contrast to the very fast methods presented, let me give you a maybe more easy to understand solution (while this in the eye of the beholder). Basically it's sort of the way I would implement it in Haskell.

Assume you want to solve your problem step by step and in each step you have your data in the form {res, in} where res is your current solution and in is the rest of the input which needs to be processed. The initial situation is that the list res contains your first element and in contains the rest of the input. Let's go through the two possible situations:

  • Since res is your current output, you always want to compare the last element of this with the first element of your in list. When the absolute difference is larger then 5, you want to append this first element of in to your res list.

  • In all other cases, you just want to throw away the first element of in and leave res untouched.

You want to iterate this as long as there are elements in your input. Thats all, and you can write it directly down:

step[{res_, in_}] := {Append[res, First[in]], Rest[in]} /; 
  Abs[Last[res] - First[in]] > 5;

step[{res_, in_}] := {res, Rest[in]};

NestWhile[step, {{100}, {102, 103, 99, 106, 107, 104, 112}}, 
 Length[Last[#]] > 0 &]

(* {{100, 106, 112}, {}} *)

You see that after your input list is empty, you have collected your result.


Something along these lines could work:

Reap[
  Sow[ z = list[[1]] ];
  Scan[If[Abs[# - z] > delta, Sow[z = #]] &, list]
][[2, 1]]

I favor Fold over Scan. I find it faster, and it does without an additional variable.

Reap[
  Fold[If[Abs[#2 - #] > delta, Sow[#2], #] &, Infinity, list];
][[2, 1]]

500 asked:

How could I now store as a sublist every element skipped in between the z points in addition to the the z point themselves. I would like to have the list of the coordinates that do not pass the test, but sublists for each group of point skipped in between two Sow[].

This is the cleanest method I could think of:

list = {100, 102, 103, 99, 106, 107, 104, 107, 112};

idx =
  Reap[i = 0;
    Fold[If[++i; Abs[#2 - #] > delta, Sow[i]; #2, #] &, Infinity, list];
  ][[2, 1]];

MapThread[list[[# ;; #2]] &, {idx, Append[Rest@idx - 1, -1]}]
{{100, 102, 103, 99}, {106, 107, 104, 107}, {112}}

The output is a list of sublists, each starting with a "z point" and continuing with intervening elements before the next point.