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 }