Yes, there is!
Szabolcs showed a use of GatherBy
in an inverted fashion as a substitute for a conventional decorate-and-sort. It proved both syntactically and computationally efficient.
By using that method in place of the decorate-and-sort in this application we can use Ordering
directly, and also eliminate Part
which was needed to strip the decoration:
myOrdering[a_] := GatherBy[Ordering @ a, a[[#]] &]
{0, 4, 1, 1, 2} // myOrdering
{{1}, {3, 4}, {5}, {2}}
This is nearly twice as fast as my old method in the question, and much shorter.
I hope this function proves to be as useful to others as I know it will be to me.
Related posts: (21453), (29551)
Applying Carl Woll's revealing method from GatherByList
to this problem we get:
myOrdering2[a_] :=
Module[{f, o = Ordering @ a},
f /: f /@ _ = a[[o]];
GatherBy[o, f]
]
This can be significantly faster in cases with heavy duplication:
big = RandomInteger[100, 1*^6];
r1 = myOrdering[big]; // RepeatedTiming // First
r2 = myOrdering2[big]; // RepeatedTiming // First
r1 === r2
0.13
0.0930
True