Is there an efficient way to find the positions of the duplicates in a list?
I would like the positions grouped according to duplicated elements. For instance, given
list = RandomInteger[15, 20]
{3, 3, 6, 11, 13, 13, 11, 1, 2, 3, 12, 8, 9, 9, 4, 15, 5, 6, 9, 12}
the output should be
positionDuplicates[list]
{{{1}, {2}, {10}}, {{3}, {18}}, {{4}, {7}}, {{5}, {6}}, {{11}, {20}}, {{13}, {14}, {19}}}
Here's my first naive thought:
positionDuplicates1[expr_] :=
Position[expr, #, 1] & /@ First /@ Select[Gather[expr], Length[#] > 1 &]
And my second:
positionDuplicates2[expr_] := Module[{seen, tags = {}},
MapIndexed[
If[seen[#1] === True, Sow[#2, #1],
If[Head[seen[#1]] === List, AppendTo[tags, #1];
Sow[seen[#1], #1]; Sow[#2, #1]; seen[#1] = True,
seen[#1] = #2]] &, expr]
]
The first works as desired but is horrible on long lists. In the second, Reap does not return positions in order, so if necessary, one can apply Sort. I feel the work done by Gather is about what it should take for this task; DeleteDuplicates is (and should be) faster.
Here is a summary of timings on a big list.
list = RandomInteger[10000, 5 10^4];
positionDuplicates[list]; // Timing
positionDuplicates2[list] // Sort; // Timing
Sort[Map[{#[[1, 1]], Flatten[#[[All, 2]]]} &, Reap[MapIndexed[Sow[{#1, #2}, #1] &, list]][[2, All, All]]]] (* Daniel Lichtblau *)
Select[Last@Reap[MapIndexed[Sow[#2, #1] &, list]], Length[#] > 1 &]; // Timing
positionOfDuplicates[list] // Sort; // Timing (* Leonid Shifrin *)
GatherBy[Range@Length[list], list[[#]] &]; // Timing (* Szabolcs *)
Gather[list]; // Timing
DeleteDuplicates[list]; // Timing
{82.378231, Null} (* my #1) {0.675543, Null} (* my #2) {0.387743, Null} (* Daniel Lichtblau *) {0.223374, Null} (* Szabolcs's suggested improvement of my #2 *) {0.062060, Null} (* Leonid Shifrin *) {0.021962, Null} (* Szabolcs's answer *) {0.009456, Null} (* Gather - for comparison purposes *) {0.000493, Null} (* DeleteDuplicates *)
seennecessary?Last@Reap[MapIndexed[Sow[#2, #1] &, list]]– Szabolcs Mar 14 at 19:58Select[result, Length[#] > 1&]or similar. – Szabolcs Mar 14 at 20:05