# Change sublist based on position

Supposed that:

list = {2, 3, 5, 7, 9};
sublist = {{3, 3, 5}, {2, 2, 2, 7, 9}};


I want to change each sublist in sublist as following:

1. Find the positions of elements in sublist in list.So,
{3, 3, 5} -> {2, 2, 3} and {2, 2, 2, 7, 9} -> {1, 1, 1, 4, 5}.

sublist1 = {{2 ,2, 3}, {1, 1, 1, 4, 5}};


2,Change each sublist in sublist1 to sparsearray:

{2, 2, 3} -> {0, 2, 1, 0, 0} and {1, 1, 1, 4, 5} -> {3, 0, 0, 1, 1}.

sublist2 = {{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}};


How to do it?

-
What should be the length of each last sublist? The same as list's? – Kuba Sep 15 '13 at 22:40
Yes.Each sublist's length is the same as list. – incognito007 Sep 15 '13 at 22:45

It's perhaps simpler to use rule replacements and the efficient Tally to get the same answer than Position (which is inefficient):

list /. Join[Rule @@@ Tally@#, {_Integer -> 0}] & /@ sublist
(* {{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}} *)

-
I must say I like it :P but also I think that this will not be the fastest solution for every variation of initial list/sublist. I suppose more answers will appear and someone will make some comparisons :p – Kuba Sep 15 '13 at 23:12
@Kuba It probably won't be the fastest, but it should be reasonably efficient, since it's not walking through list multiple times. – R. M. Sep 15 '13 at 23:15

new - all in one.

Map[Function[x, Count[x, #] & /@ list], sublist]

{{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}}


old - step by step.

1)

sublist1 = Map[Position[list, #][[1, 1]] &, sublist, {2}]

{{2, 2, 3}, {1, 1, 1, 4, 5}}


2)

sublist2 = SparseArray[Rule @@@ Tally[#], Length@list] & /@ sublist1

{SparseArray[<2>, {5}], SparseArray[<3>, {5}]}

Normal /@ sublist2

{{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}}

-
f[list_List, subList_List] := Map[Position[list, #] &, sublist, {Infinity}];? – belisarius has settled Sep 15 '13 at 22:41
@belisarius out= {{3, 3, 5}, {2, 2, 2, 7, 9}} something is not ok. – Kuba Sep 15 '13 at 22:43
Might be more efficient if you do the tally first, since position is generally an inefficient operation: Composition[Normal, SparseArray[#, Length@list]&, Position[list, #][[1, 1]] -> #2 & @@@ # &, Tally] /@ sublist – R. M. Sep 15 '13 at 22:54
@rm-rf Thanks for the remark. I've got even shorter code now :) – Kuba Sep 15 '13 at 22:58
@rm-rf Yes yes, I know, short but not effective with very sparse arrays. – Kuba Sep 15 '13 at 23:02

Here's another one-liner. Maybe not the fastest, but the most compact yet. I think it also makes it easier to understand what's being done.

Outer[Count,sublist,list,1]

{{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}}}

-
My apologies but I must condemn this method. I am well known for my fixation on terse code but I cannot excuse this; it is exactly the kind of application that I lamented in Why is there no PositionFunction in Mathematica?. (I mean no offense to you by this; it's nice clean code but I consider it a "siren song" that new users must be warned against.) – Mr.Wizard Sep 16 '13 at 7:18
@Mr.Wizard I didn't say it was efficient, but I do think it's the clearest statement yet of what is to be done. Perhaps its best use would be in a comment, along with something like "that's what we need, and here's how we get it efficiently." – Ray Koopman Sep 16 '13 at 12:18
I agree that it is the clearest code. I think it deserves more than a comment. (You'll notice that I did not down-vote this answer.) I suppose I was overly harsh yesterday, but at the same time I think there is a genuine need for a warning with such things. I think what I'll do, if I have time, is provide timings for the methods posted; then people can see for themselves how these behave without me unfairly singling out your method for criticism. – Mr.Wizard Sep 16 '13 at 16:58

I presume your starting list will never contain duplicates. Therefore:

1.

sublist1 = sublist /. MapIndexed[#1 -> First[#2] &, list]
(* {{2, 2, 3}, {1, 1, 1, 4, 5}} *)


2.

sublist2 = BinCounts[#, {1, 6, 1}] & /@ sublist1
(* {{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}} *)

-
BinCounts is/was quite slow in v7; how does it compare to Tally in v9? – Mr.Wizard Sep 16 '13 at 2:43
@Mr.Wizard been offline for a few days. In my experience BinCounts is usually slow -- I usually look for alternatives if speed is important -- but offered it here as an alternative. Whether it is viable depends on the real data to be used. – Mike Honeychurch Sep 20 '13 at 23:15

Also assuming list is duplicate free I propose this:

sparse[main_][sub_] := Tally[main ~Join~ sub][[All, 2]] - 1


Now:

sparse[list] /@ sublist

{{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}}


This works by "priming" Tally with the elements of the main list to get the order desired, then subtracting one to correct the totals. SubValues notation is used.

I like the style of the code above but this is slightly faster:

sparse2[main_, sub_] := (Tally[main ~Join~ #][[All, 2]] & /@ sub) - 1

sparse2[list, sublist]

{{0, 2, 1, 0, 0}, {3, 0, 0, 1, 1}}

-

Just another approach:

Function[u,
Reap[Sow[1, #] & /@ u, list, Total@#2 &][[2]] /. {{} -> 0, {x_} :>
x}] /@ sublist

-