# Simple way to group the list-indices of equal elements?

I am looking for a simple way to group the list-indices of equal elements of a list. Examples:

{1,2,3,3} -> {{1}, {2}, {3,4}}
(the elements at positions 1 and 2 are unique, and those at positions 3 and 4 are equal)

{f,f,g,g,f} -> {{1,2,5}, {3,4}}
(the elements at positions 1,2,5 are equal, and those at positions 3 and 4 are equal)


For now my code is

F[L_] := Values@GroupBy[MapIndexed[List, L], First -> Last, Flatten]


or

G[L_] := GatherBy[MapIndexed[List, A], First][[All, All, 2, 1]]


both of which look very clumsy. Is there a direct way to group? Something along the lines of

GroupBy[L, First -> index]


maybe?

# benchmarks

Thanks for all the contributions! Here's a ranking by runtime for specific parameters:

L = RandomInteger[{0, 10^3}, 10^6];

(* Carl's ResourceFunction call (run twice to get timing right) *)
a1 = Values@ResourceFunction["GroupByList"][Range@Length@L, L]; //AbsoluteTiming//First
(* Chris's simplest call *)
a2 = Values@PositionIndex[L]; //AbsoluteTiming//First
(* ubpdqn *)
a3 = Reap[MapIndexed[Sow[#2[], #1] &, L]][]; //AbsoluteTiming//First
(* my second crummy suggestion *)
a4 = GatherBy[MapIndexed[List, L], First][[All, All, 2, 1]]; //AbsoluteTiming//First
(* my first crummy suggestion *)
a5 = Values@GroupBy[MapIndexed[List, L], First -> Last, Flatten]; //AbsoluteTiming//First
(* KennyColnago *)
a6 = Map[SequencePosition[L, {#}][[All, 1]] &, DeleteDuplicates[L]]; //AbsoluteTiming//First
(* OkkesDulgerci *)
a7 = Flatten /@ (Position[L, #] & /@ DeleteDuplicates@L); //AbsoluteTiming//First

(*     0.028881 s for a1    *)
(*     0.086623 s for a2    *)
(*     0.997497 s for a3    *)
(*     1.44011  s for a4    *)
(*     1.8618   s for a5    *)
(*    13.86     s for a6    *)
(*    31.6595   s for a7    *)

a1 == a2 == a3 == a4 == a5 == a6 == a7
(*    True    *)


I would do it like this

Values[PositionIndex[list]]


You can use the ResourceFunction GroupByList to do this:

list = {1, 2, 3, 3};
ResourceFunction["GroupByList"][Range @ Length @ list, list]


<|1 -> {1}, 2 -> {2}, 3 -> {3, 4}|>

list = {f, f, g, g, f};
ResourceFunction["GroupByList"][Range @ Length @ list, list]


<|f -> {1, 2, 5}, g -> {3, 4}|>

Use Values to convert the Association to a list.

• Is this still faster than PositionIndex? – CA Trevillian Dec 30 '19 at 19:26
• @CATrevillian yes much faster, see benchmarks above. – Roman Dec 30 '19 at 20:50

You can also used Reap/Sow, e.g.

pi[u_] := Reap[MapIndexed[Sow[#2[], #1] &, u]][]


So pi[{f, f, g, g, f}] yields

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

I am not sure this is simple enough but here is one way to do it.

L = {1, 2, 3, 3};
G[L_] := Flatten /@ (Position[L, #] & /@ DeleteDuplicates@L)
G[L]


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

  L = {f,f,g,g,f};
G[L]


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

A solution with SequencePosition looks like:

GroupListIndices[a_List] := Map[SequencePosition[a, {#}][[All, 1]]&, DeleteDuplicates[a]]