22
$\begingroup$

I use Mathematica a little bit. I try to understand it, but it is hard

I have a list of 5 numbers selected randomly from 0 and -1. I have to replace a 0 with a 1 when the 0 is followed by -1.

My wrong solution is:

list = RandomInteger[{-1, 0}, 5] (* {0, 0, 0, -1, 0} *) list /. x_ /; x == 0 -> 1 (* {1, 1, 1, -1, 1} *) 

I need get: {0, 0, 1, -1, 0}. How do I manipulate a list to change the previous element if the next element is -1?

$\endgroup$
1

13 Answers 13

18
$\begingroup$

One of the possible solutions:

list = RandomInteger[{-1, 0}, 5] (* {0, -1, 0, 0, -1} *) list2 = list + Append[UnitStep[-1 - Differences[list]], 0] (* {1, -1, 0, 1, -1} *) 

Another two are

Partition[list, 2, 1, 1, 0] /. {{0, -1} -> 1, {n_, _} :> n} (* {1, -1, 0, 1, -1} *) f[_, n_] := n; f[-1, 0] := 1; Reverse@FoldList[f, list[[-1]], Rest@Reverse@list] (* {1, -1, 0, 1, -1} *) 

The first solution is very fast for big lists. It takes $0.08$ s for a list with $1\,000\,000$ elements. The second and the third one take $0.8$ s. Halirutan's ReplaceAll will take several hours because it has $O(n^2)$ complexity.

Update

One can use cellular automata:

CellularAutomaton[{{_, 0, -1} -> 1, {_, n_, _} :> n}, list, 1][[2]] (* {1, -1, 0, 1, -1} *) f[{n_, _}] := n; f[{0, -1}] := 1; CellularAutomaton[{f[#]&, {}, {{0}, {1}}}, list, 1][[2]] (* {1, -1, 0, 1, -1} *) 

It is not very fast ($1.8$ s for $1\,000\,000$ elements) but it shows wide opportunities of Mathematica.

$\endgroup$
16
$\begingroup$

One pattern solution is simply

list //. {s___, 0, -1, e___} :> {s, 1, -1, e} 
$\endgroup$
0
13
$\begingroup$

This should be pretty fast:

list - (list + 1) Join[Rest[list], {0}] 
$\endgroup$
1
  • $\begingroup$ @Coolwater, Join is a tiny bit faster for me. $\endgroup$ Commented Dec 8, 2013 at 19:18
11
$\begingroup$

Since no one has suggested it, there is also Developer`PartitionMap which maps a function across a partitioned list:

list = RandomInteger[{-1, 0}, 5] Clear[f]; f[{0, -1}] := 1 f[{x_, _}] := x Developer`PartitionMap[f, list, 2, 1, 1, 0] (* {0, 0, -1, 0, -1} *) (* {0, 1, -1, 1, -1} *) 
$\endgroup$
3
  • $\begingroup$ Not as fast as the pure numeric methods but far more general. +1 $\endgroup$ Commented Dec 9, 2013 at 8:07
  • $\begingroup$ @Mr.Wizard I like Developer`PartitionMap because of its relative compactness and simplicity. While not a speed demon, I think the other factors make it a nice contender. $\endgroup$ Commented Dec 9, 2013 at 14:46
  • $\begingroup$ I like it too. $\endgroup$ Commented Dec 9, 2013 at 18:16
6
$\begingroup$

I implemented a solution based on linked lists, discussed by Leonid Shifrin here.

toLinkedList[l_List] := Fold[{#2, #1} &, {}, Reverse@l] replRec[l_List] := replRec[toLinkedList[l], {}] replRec[{0, tail : {-1, _List}}, res_] := replRec[tail, {1, res}] replRec[{int_Integer, tail_List}, res_] := replRec[tail, {int, res}] replRec[{}, res_] := Reverse[Flatten[res]] 

For example

replRec[{0, 0, 0, -1, 0}] (* Out: {0, 0, 1, -1, 0} *) 

I tried this on a list of the size of 10^6, which required me to change the iteration limit:

Block[{$IterationLimit = 10^7}, replRec[list]] // AbsoluteTiming 

It took 1.34 seconds, compared to 0.39 with Michael E2's Replace method.

$\endgroup$
1
  • 1
    $\begingroup$ +1. It crossed my mind to try it here, but I didn't have the time. $\endgroup$ Commented Dec 9, 2013 at 11:15
5
$\begingroup$

Not so fast but faster than replacement rules and fun. We are going to scan on reversed list and each -1 or 0 will change the value of Sown element.

list = RandomInteger[{-1, 0}, 10] 
{-1, 0, -1, 0, -1, 0, 0, 0, -1, 0} 
f[0] := (Sow[g[0]]; g[0] = 0;); f[-1] := ((g[0] = 1); Sow[-1]) g[0] = Sow[0]; (*default value*) Reap[Scan[f, Reverse@list]][[2, 1]] // Reverse 
{-1, 1, -1, 1, -1, 0, 0, 1, -1, 0} 
$\endgroup$
5
$\begingroup$

This is just another approach:

f[0] = 1; f[-1] = -1; MapAt[f, list, Position[Rest[list], -1, {1}]] 
$\endgroup$
5
$\begingroup$

A general way with patterns, that is pretty efficient and of linear complexity. (@halirutan's is also easily adapted to general patterns). Here pat should be of the form {pat1, lookahead} -> replacement):

seqRep[list_, pat_] := (Replace[Partition[list, 2, 1], {pat, {x_, y_} :> x}, {1}]) ~Append~ Last[list] 

Example:

SeedRandom[1]; list = RandomInteger[{-1, 0}, 10] (* {0, 0, -1, 0, -1, -1, -1, 0, -1, 0} *) seqRep[list, {0, -1} -> 1] (* {0, 1, -1, 1, -1, -1, -1, 1, -1, 0} *) 

A fast way, that like others works on the OP's example of a list of integers. Its speed comes from auto-parallelization:

cf = Compile[{{x, _Integer}, {y, _Integer}}, If[x == 0 && y == -1, 1, x], RuntimeAttributes -> {Listable}, Parallelization -> True, RuntimeOptions -> "Speed", CompilationTarget -> "C" ] 

Usage:

cf[Most@list, Rest@list]~Append~Last[list] (* {0, 1, -1, 1, -1, -1, -1, 1, -1, 0} *) 

As far as speed, on a list of 10^6 integers, the Replace method took 0.42 sec., the compiled method took 0.051 sec., @ybeltukov's UnitStep method took 0.071 sec., and Simon Wood's method took 0.071 sec.

$\endgroup$
5
$\begingroup$

Using SequenceReplace

list = {0, 0, 0, -1, 0}; SequenceReplace[list, {0, -1} :> Splice @ {1, -1}] 

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

list = {0, -1, 0, 0, -1}; SequenceReplace[list, {0, -1} :> Splice @ {1, -1}] 

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

Somehow nicer (thanks bmf):

SequenceReplace[list, {0, -1} :> Sequence[1, -1]] 
  • SequenceReplace since V 11.3
  • Splice since V 12.1
$\endgroup$
1
  • 2
    $\begingroup$ (+1) and very nicely done. Perhaps you could, also, include SequenceReplace[list, {0, -1} :> Sequence[1, -1]] as a small variant $\endgroup$ Commented Dec 11, 2023 at 1:23
4
$\begingroup$

a basic solution :

 list = RandomInteger[{-1, 0}, 5] (* {0, -1, 0, 0, -1} *) Append[ Table[If[list[[i + 1]] === -1, 1, list[[i]]], {i, Length[list] - 1}], Last[list]] 

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

It is fast.

$\endgroup$
3
$\begingroup$

We can, also, use SequencePosition+ReplaceAt

list1 = {0, 0, 0, -1, 0}; list2 = {0, -1, 0, 0, -1}; ReplaceAt[list1, 0 -> 1, List /@ SequencePosition[list1, {0, -1}][[All, 1]]] ReplaceAt[list2, 0 -> 1, List /@ SequencePosition[list2, {0, -1}][[All, 1]]] 

with the results being

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

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

$\endgroup$
3
$\begingroup$

Using ReplacePart and Position:

list1 = {0, 0, 0, -1, 0}; pos[l_List] := Select[(x |-> x - 1)@Position[#, -1] &@l, Extract[l, #] == 0 &] ReplacePart[#, pos[#] -> 1] &@list1 (*{0, 0, 1, -1, 0}*) list2 = {0, -1, 0, 0, -1}; ReplacePart[#, pos[#] -> 1] &@list2 (*{1, -1, 0, 1, -1}*) list3 = {0, 0, -1, 0, -1, -1, -1, 0, -1, 0}; ReplacePart[#, pos[#] -> 1] &@list3 (*{0, 1, -1, 1, -1, -1, -1, 1, -1, 0}*) 

A procedural way using Reap and Sow:

With[{lst = list3}, Reap[ Do[ If[ i < Length[lst] && lst[[i]] == 0 && lst[[i + 1]] == -1, Sow[1], Sow[lst[[i]]] ], {i, Length[lst]} ] ][[2, 1]] ] (*{0, 1, -1, 1, -1, -1, -1, 1, -1, 0}*) 
$\endgroup$
1
$\begingroup$
list = {0, 0, 0, -1, 0}; 

Using MapAt and SequencePosition

MapAt[1 &, Most /@ SequencePosition[list, {0, -1}]] @ list 

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

$\endgroup$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.