1 /**
2     sorts stuff
3 */
4 module ddash.algorithm.sort;
5 
6 import ddash.common;
7 
8 /**
9     Might or might not sort a range, depending on some static properties of a range.
10 
11     $(LI If already sorted and no predicate then no op)
12     $(LI If not already sorted and no predicate then `sort(range)` is called)
13     $(LI If not already sorted and a predicate is provided then `sort!pred(range)` is called)
14     $(LI Else it's a no op and you get back the same range you passed in)
15 
16     Since:
17         0.0.1
18 */
19 auto ref maybeSort(alias less = null, Range)(auto ref Range range) {
20     import std.algorithm: sort;
21     import bolts.traits: isNullType;
22     import bolts.range: isSortedRange;
23     import std.functional: binaryFun;
24     static if (isNullType!less) {
25         static if (isSortedRange!Range) {
26             return range;
27         } else static if (is(typeof(sort(range)))) {
28             return sort(range);
29         } else {
30             return range;
31         }
32     } else static if (is(typeof(sort!(binaryFun!less)(range)))) {
33         return sort!(binaryFun!less)(range);
34     } else {
35         return range;
36     }
37 }
38 
39 ///
40 unittest {
41     import bolts.range: isSortedRange;
42 
43     struct A { // unsortable
44         int i;
45     }
46 
47     struct B { // sortable
48         int i;
49         bool opCmp(B a) {
50             return i < a.i;
51         }
52     }
53 
54     static assert( isSortedRange!([1].maybeSort));
55     static assert(!isSortedRange!([A()].maybeSort));
56     static assert( isSortedRange!([B()].maybeSort));
57     static assert( isSortedRange!([A()].maybeSort!"a.i < b.i"));
58 }
59 
60 
61 /**
62     Maybe sorts a range using `maybeSort` by a publicly visible member variable or property of `ElemntType!Range`
63 
64     Since:
65         0.0.1
66 */
67 auto ref maybeSortBy(string member, alias less = null, Range)(auto ref Range range) {
68     import std.algorithm: sort;
69     import bolts.traits: isNullType;
70     import bolts.range: isSortedRange;
71     import std.functional: binaryFun;
72     static if (isNullType!less) {
73         static if (is(typeof(sortBy!member(range)))) {
74             return sortBy!member(range);
75         } else {
76             return range;
77         }
78     } else static if (is(typeof(sortBy!(member, binaryFun!less)(range)))) {
79         return sortBy!(member, binaryFun!less)(range);
80     } else {
81         return range;
82     }
83 }
84 
85 ///
86 unittest {
87     import bolts.range: isSortedRange;
88 
89     struct A { // unsortable
90         int i;
91     }
92 
93     struct B { // sortable
94         int i;
95         bool opCmp(B a) {
96             return i < a.i;
97         }
98     }
99 
100     struct C {
101         B b;
102         A a;
103     }
104 
105     static assert(!isSortedRange!([C()].maybeSortBy!"a"));
106     static assert( isSortedRange!([C()].maybeSortBy!"b"));
107     static assert( isSortedRange!([C()].maybeSortBy!("a", "a.i < b.i")));
108 }
109 
110 /**
111     Sorts a range using the standard library sort by a publicly visible member variable or property of `ElemntType!Range`
112 
113     Since:
114         0.0.1
115 */
116 auto sortBy(string member, alias less = "a < b", Range)(Range range) {
117     import std.algorithm: stdSort = sort;
118     import std.functional: binaryFun;
119     import ddash.common.valueby;
120     return range.stdSort!((a, b) => binaryFun!less(valueBy!member(a), valueBy!member(b)));
121 }
122 
123 ///
124 unittest {
125     struct A {
126         int i;
127     }
128     auto a = [A(3), A(1), A(2)];
129     assert(a.sortBy!"i".equal([A(1), A(2), A(3)]));
130 }