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}.
- 136,707
- 13
- 279
- 740
- 1,661
- 1
- 11
- 14
-
2{1, 2, 3, 0, 0, 4, 5, 0, 0, 0} /. {a___, b_ /; b > 0, 0 ..} -> {a, b} – Bill Mar 24 '15 at 23:10
-
Somewhat related: (69613) – Mr.Wizard Mar 25 '15 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.
- 70,533
- 6
- 140
- 264
-
Thanks for the explanation of the code. I was completely unaware of ... Repeated. – Geoffrey Critzer Mar 24 '15 at 23:30
-
4Note 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 aShortestorLongestcorrects the problem:{0, 0, 0, 1, 2, 3, 0, 0, 4, 5} /. {Longest[0 ...], a___} :> {a}– Mr.Wizard Mar 25 '15 at 09:11
Internal`DeleteTrailingZeros@{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 = Internal`DeleteTrailingZeros@test;]
(* 0.005003 *)
First@AbsoluteTiming[res1 = dropper@test;]
(* 0.016011 *)
First@AbsoluteTiming[res2 = dropper2@test;]
(* 0.002004 *)
Equal @@ {res0, res1, res2}
(* True *)
- 394,356
- 18
- 477
- 896
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...
- 25,774
- 2
- 58
- 139
-
SparseArray[#, Automatic, 0]is somewhat verbose;Automaticand0are the defaults so you need onlySparseArray[#]. – Mr.Wizard Mar 25 '15 at 06: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}
- 41,180
- 3
- 34
- 96
-
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.) – C. E. Mar 24 '15 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. – David G. Stork Mar 24 '15 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! – Geoffrey Critzer Mar 24 '15 at 23:35
-
1Oh! 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.) – David G. Stork Mar 24 '15 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}
- 30,927
- 2
- 54
- 108
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...
- 136,707
- 13
- 279
- 740
-
3This 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:^) – Mr.Wizard Mar 25 '15 at 09:20
Using ReplaceRepeated (//.):
{1, 2, 3, 0, 0, 4, 5, 0, 0, 0} //. {a___, 0} :> {a}
{1, 2, 3, 0, 0, 4, 5}
- 23,947
- 7
- 58
- 101
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}
- 67,911
- 5
- 60
- 168
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}
- 52,495
- 4
- 30
- 85
Using FirstPosition
list = {1,2,3,0,0,4,5,0,0,0};
Drop[list,-First@FirstPosition[Reverse[list],_Integer?(#>0&)]+1]
- 146
- 4
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})
- 23,117
- 3
- 21
- 44