# assign/alter multiple entries in a list efficiently (Position is slow)

Given a list (e.g. of integers) x and a list of 0s and 1s i (of the same length as x), how can I alter those entries of x where the value of i is 1?

For example, the code

n=10^8; x=RandomInteger[{-100,100},n]; i=RandomInteger[{0,1},n];
y=RandomInteger[{-100,100},n]; ByteCount@{x,i,y}
AbsoluteTiming@MaxMemoryUsed[i=Flatten[Position[i,1],1]]
AbsoluteTiming@MaxMemoryUsed[x[[i]]=y[[i]]]


shows that for 2.4GB of data, Position spends 22sec and 10GB, whilst assignment spends only 0.85sec and 0.8GB. The former seems excessive. Is there a better way to do this? Python has this built-in (when i is a numpy array of booleans).

• why not just x = x (1 - i)?
– kglr
Oct 7, 2020 at 18:45
• x[[Pick[Range@n,i,1]]=0 Code Wolfram like Wolfram, not Python, else it's Cobols all the way down...
– ciao
Oct 7, 2020 at 19:00
• @kglr Uf, you're right, x=x*(1-i)+y*i. However, this only works if y is of the same length as x. But what if the length of y is the number of ones in i and I want to do x[[i]]=y?
– Leo
Oct 7, 2020 at 19:10
• @Leo, posted the comment as an answer.
– kglr
Oct 7, 2020 at 19:43

n = 10^8; x = RandomInteger[{-100, 100}, n]; i = RandomInteger[{0, 1}, n];
z1 = z2 = z3 = x;

AbsoluteTiming @ MaxMemoryUsed[z1 = z1 (1 - i)]

{2.00115, 1600000424}

AbsoluteTiming @ MaxMemoryUsed[z3[[RandomPrivatePositionsOf[i, 1]]] = 0]

{3.52455, 1599995920}

AbsoluteTiming @ MaxMemoryUsed[z2[[Pick[Range @ n, i, 1]]] = 0] (* ciao's comment*)

{4.8352, 1655914552}

z1 == z2 == z3

True


The last two also work for the case where "the length of y is the number of ones in i and I want to do x[[i]] = y".

• Much appreciated! Just one more question: if Position and RandomPrivatePositionsOf do the same thing, but the latter is way more effective, why doesn't it replace the former as an official command?
– Leo
Oct 7, 2020 at 20:45
• @Leo, Position handles arbitrary inputs' RandomPrivatePositionsOf (seems to be ) limited to integer vectors.
– kglr
Oct 7, 2020 at 21:47