# How to effiectively substitute the member in list with their order?

Now I have a long number list, for example

a = {{1020, 3058}, {98, 98}, {599, 600}}; (*just a sample, the list is very very long*)


I want to replace the number with their order in the list. The number 98 is the smallest number so it should be replaced with 1 and so on. After replacement the list a should be

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


The code I use is

a = {{1020, 3058}, {98, 98}, {599, 600}};
b = Sort@DeleteDuplicates@Flatten@a;
rule = Table[b[[i]] -> i, {i, 1, Length@b}];
a /. rule


or

a = {{1020, 3058}, {98, 98}, {599, 600}};
b = Sort@DeleteDuplicates@Flatten@a;
Cases[a, {x_Integer, y_Integer} :> {Position[b, x][[1, 1]], Position[b, y][[1, 1]]}]


With the increase of the list size, the code above is very time-consuming. I want to know if there some efficient way to realize my need?

I believe this is much faster:

a = {{1020, 3058}, {98, 98}, {599, 600}};
b = Sort@DeleteDuplicates@Flatten@a;
(* {{4, 5}, {1, 1}, {2, 3}} *)


To wit:

SeedRandom
a = RandomInteger[{1, 1000}, {10000, 2}];
test1 = a /. Dispatch@Thread[b -> Range@Length@b]; // AbsoluteTiming // First
test2 = a /. Thread[b -> Range@Length@b]; // AbsoluteTiming // First
test3 = Cases[a, {x_Integer, y_Integer} :> {Position[b, x][[1, 1]], Position[b, y][[1, 1]]}]; // AbsoluteTiming // First
test4 = a /. Table[b[[i]] -> i, {i, 1, Length@b}]; // AbsoluteTiming // First
(* 0.020565 *)
(* 2.091893 *)
(* 1.335496 *)
(* 1.903513 *)


Just to make sure:

test1 === test2 === test3 === test4
(* True *)


(Since I am stuck on Ver 10.0, I don't have access to RepeatedTiming, so this is the best I can do.)

• Thanks for your great answer. I use V10.0 too. – Ice0cean Nov 29 '16 at 7:40

Perhaps,

With[{u = Union @@ a}, a /. Thread[u -> Range[Length@u]]]

• In my testing, Sort@DeleteDuplicates@Flatten@a is faster than Union @@ a, but it could be my particular choice of test list (i.e. SeedRandom; a = RandomInteger[{1, 1000}, {10000, 2}];). Also, this version is not significantly faster than the OP's version. However, if instead you do Dispatch@Thread[u -> Range[Length@u]]], it is much faster. – march Nov 29 '16 at 7:22
• @march thank you...I unfortunately did not think deeply enough...I was not sure what the "size" of the problem was but accept entirely your comments. If you post Dispatch answer i am happy to +1 :) – ubpdqn Nov 29 '16 at 7:25
• Well, I did post it, but I was hesitant to, since the construction is exactly your answer, with the extra addition of the hash table created by Dispatch. – march Nov 29 '16 at 7:30
• @march sorry missed that...very instructive answer rather than my off the cuff one :) – ubpdqn Nov 29 '16 at 7:32

Here is an incremental improvement on existing answers.

march's code as fn0 for reference.

fn1 should work on lists of any shape.

fn2 assumes that your sublists are all the same length.

fn0[a_] := With[{b = Sort@DeleteDuplicates@Flatten@a},

fn1[a_] :=
a /. AssociationThread[Ordering[#], #] & @ DeleteDuplicates @ Flatten @ a;

fn2[a_] :=
Module[{flat, asc},
flat = Flatten@a;
asc = AssociationThread[Ordering[#], #] & @ DeleteDuplicates @ flat;
asc ~Lookup~ flat ~Partition~ Length[First @ a]
]

a = RandomInteger[1*^6, {500000, 2}];

fn0[a] // RepeatedTiming // First
fn1[a] // RepeatedTiming // First
fn2[a] // RepeatedTiming // First

1.53

1.33

1.09

• It's a big improvement. Thanks. Is the reason that AssociationThread is faster than Dispatch? – Ice0cean Dec 21 '16 at 4:57
• @Ice0cean If AssociationThread is faster I think (you should check) it is because we eliminate the separate Thread operation. I'll try to remember to check it myself later but I'll probably forget. – Mr.Wizard Dec 21 '16 at 5:00
• ♦ I will check it. – Ice0cean Dec 21 '16 at 5:12