0
$\begingroup$

I need to remove the zeros from the right of an integer list up to the first non-zero element. Such as {0,1,0,0,2,0,0}->{0,1,0,0,2}. Padding zeros can be simply done by PadRight, but is there any function to do the reverse?

Currently I do:

Flatten@If[MatchQ[Last@#, {0 ..}], Most@#, #] &@Split@{0, 1, 0, 0, 2, 0, 0}
(*{0,1,0,0,2}*)

Any better way of doing this?

$\endgroup$
1
  • 8
    $\begingroup$ Internal`DeleteTrailingZeros - a neat internal function I learned about from kguler - upvote that post. $\endgroup$
    – ciao
    Commented Apr 26, 2015 at 6:50

4 Answers 4

3
$\begingroup$

Here are two ideas of my own, using a recursive approach:

f1[list_] := If[Last@list === 0, f@Most[list], list]

f2[list_] := NestWhile[Most, list, Last[#] === 0 &]

I also implemented some of the methods that were already posted here:

f3[list_] := Flatten@If[MatchQ[Last@#, {0 ..}], Most@#, #] &@Split@list

f4[list_] := Replace[list, {x___, 0 ..} :> {x}]

Personally, I was most interested in how these different realizations would perform for various numbers of padding zeros. So I wrote a small testing function:

testFunc[f_] := Block[
  {list},
  list = {10 - Length[f[#]], First@AbsoluteTiming[f[#]]} & /@ 
    RandomInteger[{0, 1}, {100000, 10}];
  list = Last@Reap[Sow[#[[2]], #[[1]]] & /@ list, Range[0, 10],
    Mean[#2] &]
]

This will apply the function f to 100000 random arrays of 1s and 0s of length 10. The output is the mean absolute timing of the evaluation, where I distinguish between the number of removed 0s. Here is a comparision of the four implented functions:

ListPlot[Flatten /@ (testFunc /@ {f1, f2, f3, f4}),
  Joined -> True, InterpolationOrder -> 0, Axes -> False, Frame -> True,
  DataRange -> {0, 10}, PlotLegends -> {"f1", "f2", "f3", "f4"}]

Comparison of absolute timings for the four functions, depending on the number of padding 0s

Interestingly, there seem to be two general trends:

  • f1 and f2 both scale more or less linearly, which is to be expected of a recursive function. f2 seems to be slightly more effective. My best guess is that this can be attributed to the internal optimization for Nest.
  • f3 and f4 only depend weakly on the number of trailing 0s to be removed and even get more effective when the padding gets larger.

So, while the function definition does seem a little bit complex, the original code of Everett You does seem to be the most efficient one in most cases.

$\endgroup$
2
$\begingroup$

Do you absolutely need a built-in function? At least if performance is not a concern, this is the simplest way to do it:

{0,1,0,0,2,0,0} /. {x___, 0 ..} :> {x}

{0, 1, 0, 0, 2}

This can be written as a function, with a bit more pedantic Replace instead of /. (ReplaceAll):

RemoveRight[l_List, v_] := Replace[l, {x___, v ..} :> {x}]

RemoveRight[{0, 1, 0, 0, 2, 0, 0}, 0]

{0, 1, 0, 0, 2}

In v10.1, one can even form a replace operator from this:

RemoveZerosRight = Replace[{x___, 0 ..} :> {x}]

RemoveZerosRight[{0, 1, 0, 0, 2, 0, 0}]

{0, 1, 0, 0, 2}

$\endgroup$
1
$\begingroup$

As advised by @ciao (and introduced by kguler, I have upvoted his post: see link in comment by ciao):

Internal`DeleteTrailingZeros

and just for something ridiculous:

dz[u_] := Delete[u, -Position[Accumulate[Reverse@u], 0]]

Testing:

# -> dz@# & /@ {{0, 1, 0, 0, 2, 0, 0}, {1, 0, 0, 2, 3, 4, 0, 0, 1, 0, 
   0, 0}}

yields:

{{0, 1, 0, 0, 2, 0, 0} -> {0, 1, 0, 0, 2}, {1, 0, 0, 2, 3, 4, 0, 0, 1,
    0, 0, 0} -> {1, 0, 0, 2, 3, 4, 0, 0, 1}}
$\endgroup$
0
$\begingroup$
PadRight[{0, 1, 0, 0, 2, 0, 0}, 5]

Is this a solution?

So we can Find the first position of non-0 in Reverse Order.

list = {1, 0, 0, 2, 3, 4, 0, 0, 1, 0, 0, 0};

len = Length@list - FirstPosition[Reverse[list], x_ /; x != 0] + 1;

PadRight[list, len]

(*
    {1,0,0,2,3,4,0,0,1}
*)
$\endgroup$

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