1 #!/usr/bin/env rdmd-dev-module
2 
3 /** Extensions to std.algorithm.sort.
4     License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
5 */
6 
7 module sort_ex;
8 
9 import std.traits: isAggregateType;
10 import std.range: ElementType, isRandomAccessRange, isInputRange;
11 
12 private template xtorFun(alias xtor)
13 {
14     import std.traits: isIntegral;
15     static if (is(typeof(xtor) : string))
16     {
17         auto ref xtorFun(T)(auto ref T a)
18         {
19             mixin("with (a) { return " ~ xtor ~ "; }");
20         }
21     }
22     else static if (isIntegral!(typeof(xtor)))
23     {
24         auto ref xtorFun(T)(auto ref T a)
25         {
26             import std.conv: to;
27             mixin("return a.tupleof[" ~ xtor.to!string ~ "];");
28         }
29     }
30     else
31     {
32         alias xtorFun = xtor;
33     }
34 }
35 
36 /* private alias makePredicate(alias xtor) = (a, b) => (xtorFun!xtor(a) < xtorFun!xtor(b)); */
37 
38 /* auto sortBy(xtors..., R)(R r) { */
39 /*     alias preds = staticMap!(makePredicate, xtors); */
40 /*     return r.sort!preds; */
41 /* } */
42 
43 /** Sort Random Access Range $(D R) of Aggregates on Value of Calls to $(D xtor).
44     See also: http://forum.dlang.org/thread/nqwzojnlidlsmpunpqqy@forum.dlang.org#post-dmfvkbfhzigecnwglrur:40forum.dlang.org
45  */
46 void sortBy(alias xtor, R)(R r) if (isRandomAccessRange!R &&
47                                     isAggregateType!(ElementType!R))
48 {
49     import std.algorithm: sort;
50     import std.functional: unaryFun;
51     r.sort!((a, b) => (xtorFun!xtor(a) <
52                        xtorFun!xtor(b)));
53 }
54 
55 /** Reverse Sort Random Access Range $(D R) of Aggregates on Value of Calls to $(D xtor).
56     See also: http://forum.dlang.org/thread/nqwzojnlidlsmpunpqqy@forum.dlang.org#post-dmfvkbfhzigecnwglrur:40forum.dlang.org
57 */
58 void rsortBy(alias xtor, R)(R r) if (isRandomAccessRange!R &&
59                                      isAggregateType!(ElementType!R))
60 {
61     import std.algorithm: sort;
62     import std.functional: unaryFun;
63     r.sort!((a, b) => (xtorFun!xtor(a) >
64                        xtorFun!xtor(b)));
65 }
66 
67 @safe pure nothrow unittest
68 {
69     static struct X { int x, y, z; }
70 
71     auto r = [ X(1, 2, 1),
72                X(0, 1, 2),
73                X(2, 0, 0) ];
74 
75     r.sortBy!(a => a.x);
76     assert(r == [ X(0, 1, 2),
77                   X(1, 2, 1),
78                   X(2, 0, 0) ]);
79     r.sortBy!(a => a.y);
80     assert(r == [ X(2, 0, 0),
81                   X(0, 1, 2),
82                   X(1, 2, 1)] );
83     r.sortBy!(a => a.z);
84     assert(r == [ X(2, 0, 0),
85                   X(1, 2, 1),
86                   X(0, 1, 2) ]);
87 
88     r.sortBy!"x";
89     assert(r == [ X(0, 1, 2),
90                   X(1, 2, 1),
91                   X(2, 0, 0) ]);
92     r.sortBy!"y";
93     assert(r == [ X(2, 0, 0),
94                   X(0, 1, 2),
95                   X(1, 2, 1)] );
96     r.sortBy!"z";
97     assert(r == [ X(2, 0, 0),
98                   X(1, 2, 1),
99                   X(0, 1, 2) ]);
100 
101     r.sortBy!0;
102     assert(r == [ X(0, 1, 2),
103                   X(1, 2, 1),
104                   X(2, 0, 0) ]);
105     r.sortBy!1;
106     assert(r == [ X(2, 0, 0),
107                   X(0, 1, 2),
108                   X(1, 2, 1)] );
109     r.sortBy!2;
110     assert(r == [ X(2, 0, 0),
111                   X(1, 2, 1),
112                   X(0, 1, 2) ]);
113 }
114 
115 /** Return $(D Array) $(D r) Sorted.
116     If needed a GC-copy of $(D r) is allocated, sorted and returned.
117     See also: http://forum.dlang.org/thread/tnrvudehinmkvbifovwo@forum.dlang.org#post-tnrvudehinmkvbifovwo:40forum.dlang.org
118     TODO Add to Phobos
119 */
120 auto sorted(R, E = ElementType!R)(R r)
121 {
122     import std.traits : isNarrowString;
123     import std.range: hasLength;
124     import range_ex : isSortedRange;
125 
126     static if (isSortedRange!R)
127     {
128         return r;
129     }
130     else
131     {
132         static if (isRandomAccessRange!R)
133         {
134             auto s = r.dup;
135         }
136         else static if (isNarrowString!R)
137         {
138             import std.conv : to;
139             auto s = r.to!(dchar[]); // need dchar for random access
140         }
141         else static if (hasLength!R)
142         {
143             import std.algorithm: copy;
144             auto s = new E[r.length];
145             static if (is(typeof(r[]))) // TODO unpretty
146             {
147                 r[].copy(s);
148             }
149             else
150             {
151                 r.copy(s);
152             }
153         }
154         else
155         {
156             E[] s; // TODO use Appender?
157             foreach (const ref e; r[])
158             {
159                 s ~= e;             // TODO optimize?
160             }
161         }
162 
163         import std.algorithm.sorting : sort;
164         return sort(s);
165     }
166 }
167 
168 version(unittest) import std.algorithm.comparison : equal;
169 
170 ///
171 @safe pure unittest
172 {
173     assert(equal("öaA".sorted, "Aaö"));
174     assert(equal("öaA"w.sorted, "Aaö"));
175     assert(equal("öaA"d.sorted, "Aaö"));
176 }
177 
178 ///
179 @safe pure unittest
180 {
181     import std.algorithm.sorting : sort;
182     auto x = "öaA"d;
183     auto y = sort(x.dup).sorted; // parameter to sorted is a SortedRange
184     assert(equal(y, "Aaö"));
185 }
186 
187 ///
188 @safe pure unittest
189 {
190     import std.algorithm.sorting : sort;
191     immutable x = [3, 2, 1];
192     auto y = x.dup;
193     sort(y);
194     assert(equal(x.sorted, y));
195 }
196 
197 ///
198 unittest
199 {
200     import std.container: Array;
201     auto x = Array!int(3, 2, 1);
202     assert(equal(x.sorted, [1, 2, 3]));
203 }
204 
205 ///
206 unittest
207 {
208     import std.container: SList;
209     auto x = SList!int(3, 2, 1);
210     assert(equal(x.sorted, [1, 2, 3]));
211 }