Can Mathematica delete some of the 0's from a list?

I have a list: {1,2,3,0,0,4,5,0,0,0}. I want to delete the 0's occurring after the last positive integer. In other words, I want Mathematica to return: {1,2,3,0,0,4,5}.

• {1, 2, 3, 0, 0, 4, 5, 0, 0, 0} /. {a___, b_ /; b > 0, 0 ..} -> {a, b}
– Bill
Mar 24, 2015 at 23:10
• Somewhat related: (69613) Mar 25, 2015 at 11:21

11 Answers

The easiest way would be to use

{1, 2, 3, 0, 0, 4, 5, 0, 0, 0} /. {a___, 0 ...} :> {a}


/. is synonymous with ReplaceAll. It tries to replace values on the left hand side with the rules on the right hand side. In this case {a___, 0...} is the pattern; a___ matches zero or more elements, and 0... matches zero or more zeroes. :> {a} takes the a that corresponds to the matched expression and returns it.

• Thanks for the explanation of the code. I was completely unaware of ... Repeated. Mar 24, 2015 at 23:30
• Note that this only works because by default the first pattern is treated as Shortest. In reverse order this fails: {0, 0, 0, 1, 2, 3, 0, 0, 4, 5} /. {0 ..., a___} :> {a}. Adding a Shortest or Longest corrects the problem: {0, 0, 0, 1, 2, 3, 0, 0, 4, 5} /. {Longest[0 ...], a___} :> {a} Mar 25, 2015 at 9:11
InternalDeleteTrailingZeros@{1, 2, 3, 0, 0, 4, 5, 0, 0, 0}
(* {1, 2, 3, 0, 0, 4, 5} *)


Timing comparison with @ciao's dropper and dropper2:

test = Join[RandomChoice[{1, 10} -> {1, 0}, 100000], ConstantArray[0, 100000]];

First@AbsoluteTiming[res0 = InternalDeleteTrailingZeros@test;]
(* 0.005003 *)
First@AbsoluteTiming[res1 = dropper@test;]
(* 0.016011 *)
First@AbsoluteTiming[res2 = dropper2@test;]
(* 0.002004 *)
Equal @@ {res0, res1, res2}
(* True *)


The TakeWhile and replacement based answers will be very slow with large lists and/or large lists with many trailing zeroes.

Something like

dropper=With[{s = Split[#]}, If[s[[-1, 1]] == 0, Join @@ (Most@s), #]] &;


will be ~2000X faster on a list of 50K length vs a replacement solution, advantage growing with size. There are other even faster methods for really huge flat numeric lists... e.g.:

dropper2 =
With[{s = SparseArray[#, Automatic, 0]["NonzeroPositions"]},
If[s === {}, {}, #[[;; s[[-1, 1]]]]]] &;


Using:

test = Join[RandomChoice[{1, 10} -> {1, 0}, 100000], ConstantArray[0, 100000]];


The SparseArray is ~10X faster than dropper, which is itself ~15X faster than the TakeWhile, which is itself ~365X faster than the replace-based solution, making the SparseArray ~45,000X faster for this test...

• SparseArray[#, Automatic, 0] is somewhat verbose; Automatic and 0 are the defaults so you need only SparseArray[#]. Mar 25, 2015 at 6:55

Technically, @Bill's and @Pickett's clean answers do not quite delete the 0s after "the last positive integer." A teeny alteration fixes Bill's answer:

{1, 2, 3, 0, 0, 4, 4.2, 0, 0, 0} /. {a___, b_Integer /; b > 0, 0 ..} -> {a, b}

• What if there are negative values interspersed to the right of the last positive integer? (My answer made some assumptions about the data that I think are reasonable, so I did not address this myself.) Mar 24, 2015 at 23:17
• I read the original question to mean: "take the last positive integer in a list, and if all the remaining elements of the list are all $0$, delete them." Thus {0, -4, 0, 0} would be unchanged. I think that's what the poser meant. Mar 24, 2015 at 23:25
• For what its worth, my list only contains positive integers. My question probably should have been stated: I want to delete all the 0's after the last non-zero entry. Thank you very much! Mar 24, 2015 at 23:35
• Oh! Then that is a very simple problem and several solutions exist, such as the ones provided by @Bill and Pickett. (Try to be extremely clear and precise in your question posing from now on.) Mar 24, 2015 at 23:36
a = {1, 2, 3, 0, 0, 4, 5, 0, 0, 0};

f = Drop[#, -Length[TakeWhile[Reverse[#], Function[b, b == 0]]]] &;

f[a]


{1, 2, 3, 0, 0, 4, 5}

• +1, LengthWhile can be used here as well. Mar 25, 2015 at 0:23

Too late for the party so fun solution:

ToString @ Row @ list ~ StringTrim ~ ("0" ..) // ToExpression // IntegerDigits

{1, 2, 3, 0, 0, 4, 5}


I feel like it is a duplicate but I can't find it...

• This kills leading zero as well, which it should not, and it doesn't work with integers larger than 9. This solves both problems and it's shorter: StringTrim[ToString[list], ", 0" ... ~~ "}"] <> "}" // ToExpression :^) Mar 25, 2015 at 9:20

Using ReplaceRepeated (//.):

{1, 2, 3, 0, 0, 4, 5, 0, 0, 0} //. {a___, 0} :> {a}


{1, 2, 3, 0, 0, 4, 5}

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


Using SequencePosition (new in 10.1)

list[[;; SequencePosition[list, {a_?Positive, 0}][[-1, 1]]]]


{1, 2, 3, 0, 0, 4, 5}

Using ReplaceAt (new in 13.1)

Flatten @ ReplaceAt[{0 ...} :> Nothing, -1] @ Split[list]


{1, 2, 3, 0, 0, 4, 5}

Using IntegerExponent:

From the documentation: IntegerExponent[n,b] gives the number of trailing zeros in the digits of n in base b.

list = {1, 2, 3, 0, 0, 4, 5, 0, 0, 0};
Drop[list, -IntegerExponent[FromDigits[list]]]


{1, 2, 3, 0, 0, 4, 5}

• Very nice and a well deserved (+1). Very interesting approach
– bmf
Dec 26, 2023 at 3:09

Using FirstPosition

list = {1,2,3,0,0,4,5,0,0,0};
Drop[list,-First@FirstPosition[Reverse[list],_Integer?(#>0&)]+1]


Using ReplaceList:

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

ReplaceList[#, {a__, b_ /; b > 0, 0 ..} :> Splice@{a, b}] &@list

(*{1, 2, 3, 0, 0, 4, 5}*)


Or using SequenceCases:

SequenceCases[list, {a__, b_ /; b > 0, 0 ..} :> Splice@{a, b}]

(*{1, 2, 3, 0, 0, 4, 5}*)


Or using SequenceSplit:

First@SequenceSplit[list, s : {a__, b_} /; b > 0 :> s]

(*{1, 2, 3, 0, 0, 4, 5}*)


Or using FirstCase and Span:

#[[Span[1, #[[1]]] & @@ Position[#, FirstCase[Reverse@#, a_ /; a > 0]]]] &@list

(*{1, 2, 3, 0, 0, 4, 5}*)