1 /** Extensions to std.algorithm.sort. 2 * 3 * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 */ 5 6 module nxt.sort_ex; 7 8 import std.traits : isAggregateType; 9 import std.range.primitives : ElementType, isRandomAccessRange; 10 11 /** Sort random access range $(D R) of aggregates on value of calls to $(D xtor). 12 See_Also: http://forum.dlang.org/thread/nqwzojnlidlsmpunpqqy@forum.dlang.org#post-dmfvkbfhzigecnwglrur:40forum.dlang.org 13 */ 14 auto sortBy(alias xtor, R)(R r) 15 if (isRandomAccessRange!R && 16 isAggregateType!(ElementType!R)) 17 { 18 import std.algorithm : sort; 19 import std.functional : unaryFun; 20 return r.sort!((a, b) => (xtorFun!xtor(a) < 21 xtorFun!xtor(b))); 22 } 23 24 /** Reverse sort random access range $(D R) of aggregates on value of calls to $(D xtor). 25 See_Also: http://forum.dlang.org/thread/nqwzojnlidlsmpunpqqy@forum.dlang.org#post-dmfvkbfhzigecnwglrur:40forum.dlang.org 26 */ 27 auto rsortBy(alias xtor, R)(R r) 28 if (isRandomAccessRange!R && 29 isAggregateType!(ElementType!R)) 30 { 31 import std.algorithm : sort; 32 import std.functional : unaryFun; 33 return r.sort!((a, b) => (xtorFun!xtor(a) > 34 xtorFun!xtor(b))); 35 } 36 37 /* private alias makePredicate(alias xtor) = (a, b) => (xtorFun!xtor(a) < xtorFun!xtor(b)); */ 38 39 /// Extractor function used by `sortBy` and `rsortBy`. 40 private static template xtorFun(alias xtor) 41 { 42 import std.traits: isIntegral; 43 static if (is(typeof(xtor) : string)) 44 { 45 auto ref xtorFun(T)(auto ref T a) 46 { 47 version(LDC) pragma(inline, true); 48 mixin("with (a) { return " ~ xtor ~ "; }"); 49 } 50 } 51 else static if (isIntegral!(typeof(xtor))) 52 { 53 pragma(inline, true) // must be inlined 54 auto ref xtorFun(T)(auto ref T a) 55 { 56 import std.conv: to; 57 mixin("return a.tupleof[" ~ xtor.to!string ~ "];"); 58 } 59 } 60 else 61 { 62 alias xtorFun = xtor; 63 } 64 } 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 /** Returns: $(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 nxt.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; // TODO: remove this 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 } 212 213 import std.random : Random; 214 215 /** Functional version of `std.random.randomShuffle`. 216 * 217 * Returns: $(D r) randomly shuffled. 218 * 219 * If needed a GC-copy of $(D r) is allocated, sorted and returned. 220 */ 221 auto randomlyShuffled(Range, RandomGen)(Range r, ref RandomGen gen) 222 { 223 import std.random : randomShuffle; 224 r.randomShuffle(gen); 225 // TODO: reuse copying logic in `sorted` 226 return r; 227 } 228 229 /// 230 auto randomlyShuffled(Range)(Range r) 231 { 232 import std.random : randomShuffle; 233 r.randomShuffle(); 234 // TODO: reuse copying logic in `sorted` 235 return r; 236 } 237 238 /// 239 @safe pure unittest 240 { 241 immutable x = [3, 2, 1]; 242 auto y = x.dup; 243 Random random; 244 y.randomlyShuffled(random); 245 } 246 247 /** Sort sub-range of `subrange` defined by index range [i..j]. 248 * 249 * See_Also: https://forum.dlang.org/thread/lkggfpgvskvxvgwyjgcs@forum.dlang.org 250 */ 251 auto sortSubRange(R)(R range, size_t i, size_t j) 252 if (isRandomAccessRange!R) 253 { 254 import std.algorithm.sorting : topN, partialSort; 255 size_t start = i; 256 if (i != 0) 257 { 258 topN(range, i); // TODO: `assumePure` 259 start++; 260 } 261 partialSort(range[start .. $], j-start); 262 return range[i .. j]; 263 } 264 265 unittest 266 { 267 auto x = [1,2,7,4,2,6,8,3,9,3]; 268 auto y = sortSubRange(x, 3, 6); 269 assert(x == [2, 2, 1, 270 3, 3, 4, 271 6, 8, 9, 7]); 272 assert(y == [3, 3, 4]); 273 }