1 /** 2 Compacts a range 3 */ 4 module ddash.algorithm.compact; 5 6 /// 7 unittest { 8 import optional: no, some; 9 import ddash.utils: isFalsey; 10 11 // compact falsy values 12 assert([0, 1, 2, 0, 3].compact!isFalsey.equal([1, 2, 3])); 13 14 // compact empty arrays 15 assert([[1], [], [2]].compact!isFalsey.equal([[1], [2]])); 16 17 // compact optionals 18 assert([some(2), no!int].compact!isFalsey.equal([some(2)])); 19 20 class C { 21 int i; 22 this(int i) { this.i = i; } 23 } 24 25 import std.algorithm: map; 26 auto arr = [new C(1), null, new C(2), null]; 27 assert(arr.compact.map!"a.i".equal([1, 2])); 28 29 struct A { 30 int x; 31 } 32 33 // compact by a object member 34 assert([A(7), A(0)].compactBy!("x", isFalsey).equal([A(7)])); 35 36 // compact an associative array 37 auto aa = ["a": 1, "b": 0, "c": 2]; 38 assert(aa.compactValues!isFalsey == ["a": 1, "c": 2]); 39 } 40 41 import ddash.common; 42 43 /** 44 Compacts a range 45 46 Params: 47 pred = a unary predicate that returns true if value should be compacted 48 range = an input range 49 50 Returns: 51 compacted range 52 53 Since: 54 0.0.1 55 */ 56 auto compact(alias pred = null, Range)(Range range) if (from!"std.range".isInputRange!Range) { 57 return compactBase!("", pred)(range); 58 } 59 60 /// 61 unittest { 62 import optional: no, some; 63 import ddash.utils: isFalsey; 64 assert([0, 1, 2, 0, 3].compact!(isFalsey).equal([1, 2, 3])); 65 assert([[1], [], [2]].compact!isFalsey.equal([[1], [2]])); 66 assert([some(2), no!int].compact!isFalsey.equal([some(2)])); 67 68 class C { 69 int i; 70 this(int i) { this.i = i; } 71 } 72 73 import std.algorithm: map; 74 auto arr = [new C(1), null, new C(2), null]; 75 assert(arr.compact.map!(a => a.i).equal([1, 2])); 76 } 77 78 /** 79 Compacts a range by a publicly visible member variable or property of `ElemntType!Range` 80 81 Params: 82 member = which member in `ElementType!Range` to compact by 83 pred = a unary predicate that returns true if value should be compacted 84 range = an input range 85 86 Returns: 87 compacted range 88 89 Since: 90 0.0.1 91 */ 92 auto compactBy(string member, alias pred = null, Range)(Range range) if (from!"std.range".isInputRange!Range) { 93 return compactBase!(member, pred)(range); 94 } 95 96 private auto compactBase(string member, alias pred = null, Range)(Range range) if (from!"std.range".isInputRange!Range) { 97 import std.algorithm: filter; 98 import bolts: isNullType, isUnaryOver; 99 import ddash.common.valueby; 100 import std.range: ElementType; 101 102 alias E = ElementType!Range; 103 104 static if (isNullType!pred) 105 { 106 import bolts: isNullable; 107 static assert( 108 isNullable!E, 109 "Cannot compact non-nullable type `" ~ E.stringod ~ "'", 110 ); 111 alias fun = (a) => valueBy!member(a) !is null; 112 } 113 else static if (isUnaryOver!(pred, typeof(valueBy!member(E.init)))) 114 { 115 alias fun = a => !pred(valueBy!member(a)); 116 } 117 else 118 { 119 static assert(0, "predicate must either be null or bool function(" ~ E.stringof ~ ")"); 120 } 121 122 return range.filter!fun; 123 } 124 125 /// 126 unittest { 127 import ddash.utils: isFalsey; 128 struct A { 129 int x; 130 private int y; 131 } 132 assert([A(3, 2), A(0, 1)].compactBy!("x", isFalsey).equal([A(3, 2)])); 133 assert(!__traits(compiles, [A(3, 2)].compactBy!("y", isFalsey))); 134 assert(!__traits(compiles, [A(3, 2)].compactBy!("z", isFalsey))); 135 assert(!__traits(compiles, [A(3, 2)].compactBy!("", isFalsey))); 136 } 137 138 /** 139 Compacts an associative array by its values 140 141 Params: 142 pred = a unary predicate that returns true if value should be compacted 143 aa = compacted associated array 144 145 Returns: 146 compacted associtive array 147 148 Since: 149 0.0.1 150 */ 151 auto compactValues(alias pred = null, T, U)(T[U] aa) { 152 import std.array: byPair, assocArray; 153 return aa 154 .byPair 155 .compactBy!("value", pred) 156 .assocArray; 157 } 158 159 /// 160 unittest { 161 import ddash.utils: isFalsey; 162 auto aa = ["a": 1, "b": 0, "c": 2]; 163 assert(aa.compactValues!isFalsey == ["a": 1, "c": 2]); 164 } 165 166 /** 167 Compacts a list of values 168 169 Params: 170 values = list of values that share a common type 171 172 Returns: 173 Compacted array of values cast to common type T 174 175 Since: 176 0.0.1 177 */ 178 auto compact(alias pred = null, Values...)(Values values) if (!is(from!"std.traits".CommonType!Values == void)) { 179 import ddash.algorithm: concat; 180 return concat(values) 181 .compactBase!("", pred); 182 } 183 184 /// 185 unittest { 186 import ddash.utils: isFalsey; 187 auto a = compact!isFalsey(1, 0, 2, 0, 3); 188 auto b = compact!isFalsey(1, 0, 2.0, 0, 3); 189 190 assert(a.equal([1, 2, 3])); 191 assert(b.equal([1, 2, 3])); 192 193 static assert(is(typeof(a.array) == int[])); 194 static assert(is(typeof(b.array) == double[])); 195 }