find position of nearest nonzero element in an array (functionally)

Given an array sel and an index position i0, how can I find the position of the nearest (left or right) nonzero element? I'm able to do it with a loop and a couple of awful If's, but I was looking for a functional way...

    lr=Length[sel];
For[i = 0, i <= lr, i++,
If[1 <= i0 + i <= lr && sel[[i0 + i]] == 1, Print[i0+i]; Break[],
If[1 <= i0 - i <= lr && sel[[i0 - i]] == 1, Print[i0-i];Break[]]]]

• How large are the lists you're dealing with and how important is speed? The methods posted so far can be handily beaten, but if speed is not a primary concern, no need for added complexity. – ciao Apr 21 '15 at 22:47
• they're not big - my concern was to learn doing things functionally. Unfortunately, I find the 3 methods proposed so far a bit hard to understand: I'm working on it. Seems to me this is one of those unfortunate cases were the iterative paradigm is clearer to understand and maybe more elegant... – alessandro Apr 22 '15 at 9:08
• Don't get discouraged - functional and MMA can take some time to wrap one's head around. I'd be happy to post an "answer" that goes step-by-step on using Nearest if you'd like. Also, take a look at LengthWhile... it does what your loop does in one go. – ciao Apr 22 '15 at 22:20

3 Answers

Try this one:

nearestNonNull[lst_, i_] :=
First@MinimalBy[
Select[MapIndexed[Flatten@{#1 != 0, #2} &, lst],
TrueQ@First@# &][[All, 2]], Abs[i - #] &]

sel = RandomInteger[{0, 10}, 10^4];
nearestNonNull[sel, 1234];

nrstNZP[l_] := With[{nF = Nearest[Flatten@SparseArray[l]["NonzeroPositions"]]},
With[{nrst = nF[#, 2]}, DeleteCases[nrst, #][[1]]] & /@ #] &


Example:

SeedRandom[1]
sel = RandomInteger[{0, 2}, 20]
(* {1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 1, 1} *)
Flatten[SparseArray[sel]["NonzeroPositions"]]
(* {1, 3, 4, 8, 13, 15, 16, 19, 20} *)

nrstNZP[sel] @ Range[20]
(* {3, 1, 4, 3, 4, 4, 8, 4, 8, 8, 13, 13, 15, 13, 16, 15, 16, 19, 20, 19} *)


Here's a generalization of the above to work with arrays (lists) of arbitrary depth. Also avoids checking the element at your specified position (something which may or may not be desired).

nearestNZP =
Function[{array, i},
MinimalBy[
Flatten[MapIndexed[
If[#1 != 0 && #2 != i, #2, Unevaluated[Sequence[]]] &,
array, {Length@i}], Length@i - 1], Norm[# - i] &]];


Define array (5x5 matrix in my example):

ar = RandomInteger[{-3, 3}, {5, 5}]


{{-3, 3, -2, -2, 2}, {-1, -2, 0, -3, -3}, {-1, -1, -2, 1, 1}, {2, 3, -1, 0, 1}, {-3, 0, -3, 1, -2}}

nearestNZP[ar, {2, 2}]


{{1, 2}, {2, 1}, {3, 2}}

The depth at which the function works is determined by the length of the list of numbers specifying the index position. For a 1D array the index should be specified as nearestNZP[array, {1234}] rather than nearestNZP[array, 1234].