# Replace zeros in a list with last nonzero value

This question is closely related to the problem of zero crossings.

Suppose I have a list l = {-1, -1, 0, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, -1}

and I want to replace the zeros with the previous nonzero value, like this:

{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}

A rather clunky way to do this is with:

  f = TimeSeries[l /. 0 -> Missing[],
MissingDataMethod -> {"Interpolation", InterpolationOrder -> 0}]


Then indeed f[-1 + Range@Length@l]gives the required result.

However, there must be a more elegant way to achieve same, with patterns.

Can anyone supply a suggestion?

• What should happen when the first element is a zero? Commented Sep 25, 2019 at 14:53

### SequenceReplace

SequenceReplace[ p:{a_, 0..} :> Sequence @@ (p /. 0 -> a)] @ l


{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}

or

FixedPoint[SequenceReplace[{a_, 0} :> Sequence[a, a]], l]


{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}

### ReplaceRepeated

An alternative way to use ReplaceRepeated with a single replacement rule:

l //. {a___, b_, 0, c___} :> {a, b, b, c}


{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}

### Memoization

Memoize the last non-zero value (inspired by @WReach's answer):

f[x_] := (f[0] = x; x)
f /@ l


{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}

### Split + ReplaceAll

Split[l, #2 == 0 &] /. p : {a_, 0 ..} :> (p /. 0 -> a) // Flatten


{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}

### TimeSeries + MissingDataMethod

Using the "Values" property with OP's f:

f["Values"]


{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}

• One of the reasons I asked this question even though I had a working (although inelegant) solution was to see all the creative ways in which MMA experts are able to use the Wolfram language to solve a problem. Outstanding! Commented Sep 25, 2019 at 22:41
• What sort of (Mathematica) black magic is this!? My +1 sir. Commented Sep 27, 2019 at 13:54

Define a simple function, using FoldList

op = FoldList[If[#2 == 0, #1, #2] &];

l = {-1, -1, 0, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, -1};

op@l
(* {-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1} *)

• This must be the fastest (and simplest) of the lot. The one I knew existed, but never reached. Commented Sep 26, 2019 at 22:19

Perhaps something like this will appeal

l //. {{a___, 1, 0, b___} -> {a, 1, 1, b}, {a___, -1, 0, b___} -> {a, -1, -1, b}}

• This is the kind of thing I was aiming for, but I couldn't find the right formulation. I find patterns such a challenge (although I do use them extensively). Anyway that you for a great solution. Commented Sep 25, 2019 at 22:35

Given:

list = {-1, -1, 0, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, -1}


Then:

Module[{prev = 0}, Replace[list, {0 :> prev, x_ :> (prev = x)}, {1}]]

(* {-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1} *)


The initial value assigned by prev = 0 is only used for lists that start with a zero -- choose another value if desired.

A few more...

l = {-1, -1, 0, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, -1};

Do[If[l[[i]] == 0, l[[i]] = l[[i - 1]]], {i, 2, Length[l]}]

l


{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}

fn[l_] := Block[{l1 = l},
Set[Part[l1, #[[2]]], Part[l1, #[[1]]]] & /@ SequencePosition[l, {_, 0}];
l1
]

l = {-1, -1, 0, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, -1};

FixedPoint[fn, l]


{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}

l = {-1, -1, 0, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, -1};

Fold[{#1, If[#2 == 0, Last[#1], #2]} &, l[[1 ;; 1]], l[[2 ;; -1]]] //Flatten


{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}

list = {-1, -1, 0, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, -1};


Using SplitBy, SequenceCases and a suggestion from Syed:

patts = {{0 ..}, {a : Except[0], b : 0 ..} :> Array[a &, Length@{b}]};

sp = SplitBy[#, {0 ..}] &@list;

Catenate[sp /. Rule @@@ Thread[SequenceCases[list, #] & /@ patts]]

(*{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}*)


Or in a simpler way using SequenceReplace, as @Syed suggests:

patt = {a : Except[0], b : 0 ..} :> Splice@Array[a &, Length@{b} + 1];

SequenceReplace[list, patt]

(*{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}*)

• SequenceReplace[list, {a : Except[0], b : 0 ..} :> Sequence @@ ConstantArray[a, Length@{b} + 1]]. ReplacePart functionality is built into Sequence* functions. Flatten is not a good idea. Consider replacing the -1 in the second location with {a} and rerun the solutions.
– Syed
Commented Feb 7 at 3:42
• Thanks for your excellent observation, Syed! I think I'll pay more attention to SequenceReplace because I haven't used it much. See the update, please. :-) Commented Feb 7 at 4:39
list = {-1, -1, 0, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, -1};


Using SequenceSplit (new in 11.3)

Flatten @ SequenceSplit[list, {a_, b : 0 ..} :> {a, Table[a, Length @ {b}]}]


{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}

list = {-1, -1, 0, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, -1};


Using SequenceSplit with ReplaceAll

Flatten @ SequenceSplit[list, {a_, b : 0 ..} :> {a, {b} /. 0 :> a}]


{-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1}

Looking for a mostly arithmetic solution, I found a function that replaces only the first zero:

f[list_] := list + Prepend[ Rest[1 - Unitize[list]] Most[list], 0]


Finding the number of repetitions requires patterns:

list = {-1, -1, 0, 1, 0, 0, -1, -1, 1, 1, 1, 0, 0, 0, -1};
numberIterations =  Max[ SequenceCases[list, {p : Repeated[0]} :> Length[{p}]]]
Nest[f, list, numberIterations]
(* {-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1} *)


Works, but it didn't correspond to what I was looking for.