1 /** 2 * Associative Array utility functions 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.aa; 15 16 import std.algorithm; 17 import std.range; 18 19 // *************************************************************************** 20 21 /// Get a value from an AA, and throw an exception (not an error) if not found 22 ref V aaGet(K, V)(V[K] aa, K key) 23 { 24 import std.conv; 25 26 auto p = key in aa; 27 if (p) 28 return *p; 29 else 30 static if (is(typeof(text(key)))) 31 throw new Exception("Absent value: " ~ text(key)); 32 else 33 throw new Exception("Absent value"); 34 } 35 36 /// If key is not in aa, add it with defaultValue. 37 /// Returns a reference to the value corresponding to key. 38 ref V getOrAdd(K, V)(ref V[K] aa, K key, V defaultValue = V.init) 39 { 40 auto p = key in aa; 41 if (!p) 42 { 43 aa[key] = defaultValue; 44 p = key in aa; 45 } 46 return *p; 47 } 48 49 unittest 50 { 51 int[int] aa; 52 aa.getOrAdd(1, 2) = 3; 53 assert(aa[1] == 3); 54 assert(aa.getOrAdd(1, 4) == 3); 55 } 56 57 struct KeyValuePair(K, V) { K key; V value; } 58 59 /// Get key/value pairs from AA 60 KeyValuePair!(K, V)[] pairs(K, V)(V[K] aa) 61 { 62 KeyValuePair!(K, V)[] result; 63 foreach (key, value; aa) 64 result ~= KeyValuePair!(K, V)(key, value); 65 return result; 66 } 67 68 /// Get key/value pairs from AA, sorted by keys 69 KeyValuePair!(K, V)[] sortedPairs(K, V)(V[K] aa) 70 { 71 KeyValuePair!(K, V)[] result; 72 foreach (key; aa.keys.sort) 73 result ~= KeyValuePair!(K, V)(key, aa[key]); 74 return result; 75 } 76 77 /// Get values from AA, sorted by keys 78 V[] sortedValues(K, V)(V[K] aa) 79 { 80 V[] result; 81 foreach (key; aa.keys.sort) 82 result ~= aa[key]; 83 return result; 84 } 85 86 /// Merge source into target. Return target. 87 V[K] merge(K, V)(auto ref V[K] target, in V[K] source) 88 { 89 foreach (k, v; source) 90 target[k] = v; 91 return target; 92 } 93 94 unittest 95 { 96 int[int] target; 97 int[int] source = [2:4]; 98 merge(target, source); 99 assert(source == target); 100 101 target = [1:1, 2:2, 3:3]; 102 merge(target, source); 103 assert(target == [1:1, 2:4, 3:3]); 104 105 assert(merge([1:1], [2:2]) == [1:1, 2:2]); 106 } 107 108 // *************************************************************************** 109 110 /// An associative array which retains the order in which elements were added. 111 struct OrderedMap(K, V) 112 { 113 K[] keys; 114 V[] values; 115 size_t[K] index; 116 117 ref V opIndex(ref K k) 118 { 119 return values[index[k]]; 120 } 121 122 ref V opIndexAssign()(auto ref V v, auto ref K k) 123 { 124 auto pi = k in index; 125 if (pi) 126 { 127 auto pv = &values[*pi]; 128 *pv = v; 129 return *pv; 130 } 131 132 index[k] = values.length; 133 keys ~= k; 134 values ~= v; 135 return values[$-1]; 136 } 137 138 void remove()(auto ref K k) 139 { 140 auto i = index[k]; 141 index.remove(k); 142 keys = keys.remove(i); 143 values = values.remove(i); 144 } 145 146 @property size_t length() { return values.length; } 147 148 int opApply(int delegate(ref K k, ref V v) dg) 149 { 150 int result = 0; 151 152 foreach (i, ref v; values) 153 { 154 result = dg(keys[i], v); 155 if (result) 156 break; 157 } 158 return result; 159 } 160 } 161 162 unittest 163 { 164 OrderedMap!(string, int) m; 165 m["a"] = 1; 166 m["b"] = 2; 167 m["c"] = 3; 168 assert(m.length == 3); 169 m.remove("a"); 170 assert(m.length == 2); 171 } 172 173 // *************************************************************************** 174 175 /// Helper/wrapper for void[0][T] 176 struct HashSet(T) 177 { 178 void[0][T] data; 179 180 alias data this; 181 182 this(R)(R r) 183 { 184 foreach (k; r) 185 add(k); 186 } 187 188 void add(T k) 189 { 190 void[0] v; 191 data[k] = v; 192 } 193 194 void remove(T k) 195 { 196 data.remove(k); 197 } 198 199 @property HashSet!T dup() const 200 { 201 // Can't use .dup with void[0] value 202 HashSet!T result; 203 foreach (k, v; data) 204 result.add(k); 205 return result; 206 } 207 208 int opApply(scope int delegate(ref T) dg) 209 { 210 int result; 211 foreach (k, v; data) 212 if ((result = dg(k)) != 0) 213 break; 214 return result; 215 } 216 } 217 218 unittest 219 { 220 HashSet!int s; 221 assert(s.length == 0); 222 assert(!(1 in s)); 223 assert(1 !in s); 224 s.add(1); 225 assert(1 in s); 226 assert(s.length == 1); 227 foreach (k; s) 228 assert(k == 1); 229 s.remove(1); 230 assert(s.length == 0); 231 232 s.add(1); 233 auto t = s.dup; 234 s.add(2); 235 assert(t.length==1); 236 t.remove(1); 237 assert(t.length==0); 238 } 239 240 auto toSet(R)(R r) 241 { 242 alias E = ElementType!R; 243 return HashSet!E(r); 244 } 245 246 unittest 247 { 248 auto set = [1, 2, 3].toSet(); 249 assert(2 in set); 250 assert(4 !in set); 251 }