Simon Woods already gave what is arguably the canonical answer, but I always like seeing and sharing alternatives. Since one of the operations here is a duplicate-removal we can adapt some of the methods described in Delete duplicate elements from a list.
SeedRandom[3]
a = RandomInteger[5, {9, 2}];
(* {{3, 5}, {0, 1}, {2, 0}, {0, 4}, {5, 2}, {2, 2}, {1, 0}, {0, 2}, {1, 3}} *)
Sequence
Module[{f},
f[d : {_, x_}] := (f[{_, x}] = Sequence[]; d);
SortBy[f /@ a, Last]
]
{{2, 0}, {0, 1}, {5, 2}, {1, 3}, {0, 4}, {3, 5}}
Sow & Reap
Reap[Sow @@@ a, _, {#2[[1]], #} &][[2]] ~SortBy~ Last
Sow
and Reap
are also conducive to a form with integrated sorting:
Reap[Sow @@@ a, Union[Last /@ a], {#2[[1]], #} &][[2, All, 1]]
Tally
The Tally
method ("For Mathematica 6") is analogous to Simon's use of GatherBy
:
GatherBy[a, Last][[All, 1]] ~SortBy~ Last
Rules
With this method it makes more sense to use integrated sorting than not:
Union @ a[[All, 2]] /. Dispatch[#2 -> {##} & @@@ a]
In a maximally terse form (without the performance of Dispatch
) this is the shortest method of all:
Union[Last/@a]/.#2->{##}&@@@a
Another form with an arbitrary function in place of last
:
suBy[a_, f_] := Union[#] /. Thread[# -> a] &[f /@ a]
suBy[a, Last]
Performance comparison
The Sequence
method is omitted as it is very slow. First on a set with heavy duplication:
a = RandomInteger[1000, {500000, 2}];
Reap[Sow @@@ a, _, {#2[[1]], #} &][[2]] ~SortBy~ Last // Timing // First
Reap[Sow @@@ a, Union[Last /@ a], {#2[[1]], #} &][[2, All, 1]] // Timing // First
GatherBy[a, Last][[All, 1]] ~SortBy~ Last // Timing // First
Union @ a[[All, 2]] /. Dispatch[#2 -> {##} & @@@ a] // Timing // First
0.171
0.266
0.046
1.857
Then a set with limited duplication:
a = RandomInteger[300000, {150000, 2}];
Reap[Sow @@@ a, _, {#2[[1]], #} &][[2]] ~SortBy~ Last // Timing // First
Reap[Sow @@@ a, Union[Last /@ a], {#2[[1]], #} &][[2, All, 1]] // Timing // First
GatherBy[a, Last][[All, 1]] ~SortBy~ Last // Timing // First
Union @ a[[All, 2]] /. Dispatch[#2 -> {##} & @@@ a] // Timing // First
1.389
1.046
0.375
0.483
Plainly GatherBy
is best in either case but the alternatives are more competitive here.
SplitBy
Szabolcs proposed a different form with the claim that "it might be theoretically a bit more efficient."
Here are some Timings comparing it to the GatherBy
method. Also, SortBy[. . ., Last]
is not a stable sort, so I shall additionally include a variation with {Last}
; this is arguably more "correct" and also often more efficient.
With heavy duplication:
a = RandomInteger[1000, {500000, 2}];
GatherBy[a, Last][[All, 1]] ~SortBy~ Last // Timing // First
SplitBy[SortBy[a, Last], Last][[All, 1]] // Timing // First
SplitBy[SortBy[a, {Last}], Last][[All, 1]] // Timing // First
0.05992
0.593
0.514
Here SplitBy
is an order of magnitude slower; the stable version is a bit faster but not by a lot.
With limited duplication:
a = RandomInteger[300000, {150000, 2}];
GatherBy[a, Last][[All, 1]] ~SortBy~ Last // Timing // First
SplitBy[SortBy[a, Last], Last][[All, 1]] // Timing // First
SplitBy[SortBy[a, {Last}], Last][[All, 1]] // Timing // First
0.468
0.375
0.343
Here SplitBy
is faster but not nearly to the degree that it was slower in the prior example. The stable version is again slightly faster.
With non-packed String data (moderately heavy duplication):
a = FromCharacterCode /@ RandomInteger[{97, 122}, {150000, 2, 3}];
GatherBy[a, Last][[All, 1]] ~SortBy~ Last // Timing // First
SplitBy[SortBy[a, Last], Last][[All, 1]] // Timing // First
SplitBy[SortBy[a, {Last}], Last][[All, 1]] // Timing // First
0.0812
0.437
0.2152
GatherBy
wins, but perhaps more interesting the stable sort is now twice as efficient as the original version.
{{b,3.04},{a,3.10}}
. If not, and You want to delete duplicates in second part then why "d" is better than "c"? Why the answer couldn't be{{d,3},{b,3.04},{a,3.10}}
? $\endgroup$