4

Certainly, a duplicate but I have not found it:

I have two lists

u = {2,3,0,0}
v = {4,0,0,6} 

I want the zeros of the first at the same place that the zeros of the second

I have tried with Sort with Position but in that case the order matters. I want only the position --- it may be other numbers than zeros.

cyrille.piatecki
  • 4,582
  • 13
  • 26
  • 5
    What is the expected result? Will the count of zeros be always the same? – Kuba Jul 18 '17 at 07:39
  • 3
    You are clearly around voting and accepting, is there any reason why you are ignoring my questions? – Kuba Jul 19 '17 at 09:07

6 Answers6

8
sa = SparseArray;
sa[sa[v]["NonzeroPositions"] -> sa[u]["NonzeroValues"], Length[v]] // Normal

{2, 0, 0, 3}

Update: Turning the method above into a function and adding two more alternatives

ClearAll[f1,f2,f3]
f1 = Module[{pos = SparseArray[#]["NonzeroPositions"], 
     vals = SparseArray[#2]["NonzeroValues"]}, Normal@SparseArray[pos->vals, Length@#]]&;
f2 = Module[{w = #, pos = Position[#, Except[0], 1, Heads -> False], 
     vals = DeleteCases[#2, 0]}, MapThread[(w[[#]] = #2)&, {pos, vals}]; w]&;
f3 = Module[{i = 1, w = #, pos = Position[#,Except[0],1, Heads -> False], 
     vals = DeleteCases[#2, 0]}, MapAt[vals[[i++]]&, w, pos]]&;

Examples:

u = {2, 3, 0, 0};
v = {4, 0, 0, 6} ;
f1[v, u]

{2, 0, 0, 3}

f1[u, v]

{4, 6, 0, 0}

Equal @@ (#[v, u] & /@ {f1, f2, f3})

True

kglr
  • 394,356
  • 18
  • 477
  • 896
7
u = {2, 3, 0, 0};
v = {4, 0, 0, 6};

Fold[Insert[#1, 0, #2] &, DeleteCases[u, 0], Position[v, 0]]

{2, 0, 0, 3}

Lists can have different lengths

u = {1, 2, 3};
v = {0, 0};

{0, 0, 1, 2, 3}

eldo
  • 67,911
  • 5
  • 60
  • 168
5
Module[{i = 0}
  , Replace[v, Except[0] :> #[[++i]], {1}] & @ DeleteCases[u, 0]
]

{2, 0, 0, 3}

Kuba
  • 136,707
  • 13
  • 279
  • 740
4

This works only if Count[u, 0] === Count[v, 0]

sz = 20;
u = RandomInteger[{1, sz}, sz] (zeros = RandomInteger[1, sz])
v = RandomInteger[{1, sz}, sz] RandomSample[zeros]

res = u[[Ordering[Sign[u]][[Ordering[Ordering[Sign[v]]]]]]]

If Count[u, 0] > Count[v, 0], I find it most intuitive to match/reposition the first Count[v, 0] zeros of u while keeping the remaining zeros in place, and when Count[u, 0] < Count[v, 0] to match all zeros of u with the first Count[u, 0] zeros of v. E.g.
u = {2,3,0,0}; v = {4,0,1,6} yields {2,0,3,0} and
u = {2,3,1,0}; v = {4,0,0,6} yields {2,0,3,1} or in code:

Evaluate[PadRight[DeleteCases[Insert[u[[Ordering[-Sign[u]]]],
  #, # - Range[0, Length[#] - 1] &[Take[Position[v, 0, {1}],
    UpTo[Count[u, 0]]]]], 0], Length[v], 0]] &[0]

For all cases if u and v contain negative numbers, you need to replace Sign by Abs@*Sign

Coolwater
  • 20,257
  • 3
  • 35
  • 64
2

Using pattern matching on the internal form of SparseArray:

u = {6, 1, 0, 0, 2, 0, -3, -9};
v = {3, -2, 1, 5, 0, 4, 0, 0};

SparseArray /@ {u, v} /.
  {_[__, {__, k_}], a_[i__, {j__, _}]} :> a[i, {j, k}] // Normal
{6, 1, 2, -3, 0, -9, 0, 0}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
1

Terse in-place form of Coolwater's method:

u = {2, 3, 0, 0};
v = {4, 0, 0, 6};

(u[[#]] = u[[#2]]) & @@ Ordering /@ Unitize@{u, v};

u
{2, 0, 0, 3}

Works with negatives by default:

u = {6, 1, 0, 0, 2, 0, -3, -9};
v = {3, -2, 1, 5, 0, 4, 0, 0};

(u[[#]] = u[[#2]]) & @@ Ordering /@ Unitize@{u, v};

u
{6, 1, 2, -3, 0, -9, 0, 0}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371