2
$\begingroup$

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[]]]]
$\endgroup$
  • $\begingroup$ 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. $\endgroup$ – ciao Apr 21 '15 at 22:47
  • $\begingroup$ 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... $\endgroup$ – alessandro Apr 22 '15 at 9:08
  • $\begingroup$ 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. $\endgroup$ – ciao Apr 22 '15 at 22:20
1
$\begingroup$

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];
$\endgroup$
1
$\begingroup$
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} *)
$\endgroup$
0
$\begingroup$

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].

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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