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 import std.typecons; 19 20 // *************************************************************************** 21 22 /// Get a value from an AA, and throw an exception (not an error) if not found 23 ref auto aaGet(AA, K)(AA aa, K key) 24 if (is(typeof(key in aa))) 25 { 26 import std.conv; 27 28 auto p = key in aa; 29 if (p) 30 return *p; 31 else 32 static if (is(typeof(text(key)))) 33 throw new Exception("Absent value: " ~ text(key)); 34 else 35 throw new Exception("Absent value"); 36 } 37 38 /// If key is not in aa, add it with defaultValue. 39 /// Returns a reference to the value corresponding to key. 40 ref V getOrAdd(K, V)(ref V[K] aa, K key, V defaultValue = V.init) 41 { 42 auto p = key in aa; 43 if (!p) 44 { 45 aa[key] = defaultValue; 46 p = key in aa; 47 } 48 return *p; 49 } 50 51 unittest 52 { 53 int[int] aa; 54 aa.getOrAdd(1, 2) = 3; 55 assert(aa[1] == 3); 56 assert(aa.getOrAdd(1, 4) == 3); 57 } 58 59 struct KeyValuePair(K, V) { K key; V value; } 60 61 /// Get key/value pairs from AA 62 KeyValuePair!(K, V)[] pairs(K, V)(V[K] aa) 63 { 64 KeyValuePair!(K, V)[] result; 65 foreach (key, value; aa) 66 result ~= KeyValuePair!(K, V)(key, value); 67 return result; 68 } 69 70 /// Get key/value pairs from AA, sorted by keys 71 KeyValuePair!(K, V)[] sortedPairs(K, V)(V[K] aa) 72 { 73 KeyValuePair!(K, V)[] result; 74 foreach (key; aa.keys.sort) 75 result ~= KeyValuePair!(K, V)(key, aa[key]); 76 return result; 77 } 78 79 /// Get values from AA, sorted by keys 80 V[] sortedValues(K, V)(in V[K] aa) 81 { 82 V[] result; 83 foreach (key; aa.keys.sort()) 84 result ~= aa[key]; 85 return result; 86 } 87 88 /// Merge source into target. Return target. 89 V[K] merge(K, V)(auto ref V[K] target, in V[K] source) 90 { 91 foreach (k, v; source) 92 target[k] = v; 93 return target; 94 } 95 96 unittest 97 { 98 int[int] target; 99 int[int] source = [2:4]; 100 merge(target, source); 101 assert(source == target); 102 103 target = [1:1, 2:2, 3:3]; 104 merge(target, source); 105 assert(target == [1:1, 2:4, 3:3]); 106 107 assert(merge([1:1], [2:2]) == [1:1, 2:2]); 108 } 109 110 /// Slurp a range of two elements (or two-element struct/class) into an AA. 111 auto toAA(R)(R r) 112 if (is(typeof(r.front[1]))) 113 { 114 alias K = typeof(r.front[0]); 115 alias V = typeof(r.front[1]); 116 V[K] result; 117 foreach (pair; r) 118 { 119 assert(pair.length == 2); 120 result[pair[0]] = pair[1]; 121 } 122 return result; 123 } 124 125 /// ditto 126 auto toAA(R)(R r) 127 if (is(typeof(r.front.tupleof)) && r.front.tupleof.length == 2 && !is(typeof(r.front[1]))) 128 { 129 return r.map!(el => tuple(el.tupleof)).toAA(); 130 } 131 132 unittest 133 { 134 assert([[2, 4]].toAA() == [2:4]); 135 assert([2:4].pairs.toAA() == [2:4]); 136 } 137 138 // *************************************************************************** 139 140 /// An associative array which retains the order in which elements were added. 141 struct OrderedMap(K, V) 142 { 143 K[] keys; 144 V[] values; 145 size_t[K] index; 146 147 ref inout(V) opIndex()(auto ref K k) inout 148 { 149 return values[index[k]]; 150 } 151 152 ref V opIndexAssign()(auto ref V v, auto ref K k) 153 { 154 auto pi = k in index; 155 if (pi) 156 { 157 auto pv = &values[*pi]; 158 *pv = v; 159 return *pv; 160 } 161 162 index[k] = values.length; 163 keys ~= k; 164 values ~= v; 165 return values[$-1]; 166 } 167 168 ref V getOrAdd()(auto ref K k) 169 { 170 auto pi = k in index; 171 V* pv; 172 if (pi) 173 pv = &values[*pi]; 174 else 175 { 176 index[k] = values.length; 177 keys ~= k; 178 values ~= V.init; 179 pv = &values[$-1]; 180 } 181 return *pv; 182 } 183 184 ref V opIndexUnary(string op)(auto ref K k) 185 { 186 auto pv = &getOrAdd(k); 187 mixin("(*pv) " ~ op ~ ";"); 188 return *pv; 189 } 190 191 ref V opIndexOpAssign(string op)(auto ref V v, auto ref K k) 192 { 193 auto pv = &getOrAdd(k); 194 mixin("(*pv) " ~ op ~ "= v;"); 195 return *pv; 196 } 197 198 inout(V) get()(auto ref K k, inout(V) defaultValue) inout 199 { 200 auto p = k in index; 201 return p ? values[*p] : defaultValue; 202 } 203 204 inout(V)* opIn_r()(auto ref K k) inout 205 { 206 auto p = k in index; 207 return p ? &values[*p] : null; 208 } 209 210 void remove()(auto ref K k) 211 { 212 auto i = index[k]; 213 index.remove(k); 214 keys = keys.remove(i); 215 values = values.remove(i); 216 } 217 218 @property size_t length() const { return values.length; } 219 220 private int opApplyImpl(this This, Dg)(Dg dg) 221 { 222 int result = 0; 223 224 foreach (i, ref v; values) 225 { 226 result = dg(keys[i], v); 227 if (result) 228 break; 229 } 230 return result; 231 } 232 233 int opApply(int delegate(ref K k, ref V v) dg) 234 { 235 return opApplyImpl(dg); 236 } 237 238 int opApply(int delegate(const ref K k, const ref V v) dg) const 239 { 240 return opApplyImpl(dg); 241 } 242 243 @property typeof(this) dup() 244 { 245 typeof(this) result; 246 result.keys = keys.dup; 247 result.values = values.dup; 248 result.index = index.dup; 249 return result; 250 } 251 } 252 253 unittest 254 { 255 OrderedMap!(string, int) m; 256 m["a"] = 1; 257 m["b"] = 2; 258 m["c"] = 3; 259 assert(m.length == 3); 260 assert("a" in m); 261 assert("d" !in m); 262 m.remove("a"); 263 assert(m.length == 2); 264 m["x"] -= 1; 265 assert(m["x"] == -1); 266 ++m["y"]; 267 assert(m["y"] == 1); 268 auto cm = cast(const)m.dup; 269 foreach (k, v; cm) 270 if (k == "x") 271 assert(v == -1); 272 } 273 274 // *************************************************************************** 275 276 /// Helper/wrapper for void[0][T] 277 struct HashSet(T) 278 { 279 void[0][T] data; 280 281 alias data this; 282 283 this(R)(R r) 284 { 285 foreach (k; r) 286 add(k); 287 } 288 289 void add(T k) 290 { 291 void[0] v; 292 data[k] = v; 293 } 294 295 void remove(T k) 296 { 297 data.remove(k); 298 } 299 300 @property HashSet!T dup() const 301 { 302 // Can't use .dup with void[0] value 303 HashSet!T result; 304 foreach (k, v; data) 305 result.add(k); 306 return result; 307 } 308 309 int opApply(scope int delegate(ref T) dg) 310 { 311 int result; 312 foreach (k, v; data) 313 if ((result = dg(k)) != 0) 314 break; 315 return result; 316 } 317 } 318 319 unittest 320 { 321 HashSet!int s; 322 assert(s.length == 0); 323 assert(!(1 in s)); 324 assert(1 !in s); 325 s.add(1); 326 assert(1 in s); 327 assert(s.length == 1); 328 foreach (k; s) 329 assert(k == 1); 330 s.remove(1); 331 assert(s.length == 0); 332 333 s.add(1); 334 auto t = s.dup; 335 s.add(2); 336 assert(t.length==1); 337 t.remove(1); 338 assert(t.length==0); 339 } 340 341 auto toSet(R)(R r) 342 { 343 alias E = ElementType!R; 344 return HashSet!E(r); 345 } 346 347 unittest 348 { 349 auto set = [1, 2, 3].toSet(); 350 assert(2 in set); 351 assert(4 !in set); 352 } 353 354 // *************************************************************************** 355 356 /// An object which acts mostly as an associative array, 357 /// with the added property of being able to hold keys with 358 /// multiple values. These are only exposed explicitly and 359 /// through iteration 360 struct MultiAA(K, V) 361 { 362 V[][K] items; 363 364 /// If multiple items with this name are present, 365 /// only the first one is returned. 366 ref inout(V) opIndex(K key) inout 367 { 368 return items[key][0]; 369 } 370 371 V opIndexAssign(V value, K key) 372 { 373 items[key] = [value]; 374 return value; 375 } 376 377 inout(V)* opIn_r(K key) inout @nogc 378 { 379 auto pvalues = key in items; 380 if (pvalues && (*pvalues).length) 381 return &(*pvalues)[0]; 382 return null; 383 } 384 385 void remove(K key) 386 { 387 items.remove(key); 388 } 389 390 // D forces these to be "ref" 391 int opApply(int delegate(ref K key, ref V value) dg) 392 { 393 int ret; 394 outer: 395 foreach (key, values; items) 396 foreach (ref value; values) 397 { 398 ret = dg(key, value); 399 if (ret) 400 break outer; 401 } 402 return ret; 403 } 404 405 // Copy-paste because of https://issues.dlang.org/show_bug.cgi?id=7543 406 int opApply(int delegate(ref const(K) key, ref const(V) value) dg) const 407 { 408 int ret; 409 outer: 410 foreach (key, values; items) 411 foreach (ref value; values) 412 { 413 ret = dg(key, value); 414 if (ret) 415 break outer; 416 } 417 return ret; 418 } 419 420 void add(K key, V value) 421 { 422 if (key !in items) 423 items[key] = [value]; 424 else 425 items[key] ~= value; 426 } 427 428 V get(K key, lazy V def) const 429 { 430 auto pvalue = key in this; 431 return pvalue ? *pvalue : def; 432 } 433 434 inout(V)[] getAll(K key) inout 435 { 436 inout(V)[] result; 437 foreach (ref value; items.get(key, null)) 438 result ~= value; 439 return result; 440 } 441 442 this(typeof(null) Null) 443 { 444 } 445 446 this(V[K] aa) 447 { 448 foreach (ref key, ref value; aa) 449 add(key, value); 450 } 451 452 this(V[][K] aa) 453 { 454 foreach (ref key, values; aa) 455 foreach (ref value; values) 456 add(key, value); 457 } 458 459 @property auto keys() inout { return items.keys; } 460 461 // https://issues.dlang.org/show_bug.cgi?id=14626 462 463 @property V[] values() 464 { 465 return items.byValue.join; 466 } 467 468 @property const(V)[] values() const 469 { 470 return items.byValue.join; 471 } 472 473 @property typeof(V[K].init.pairs) pairs() 474 { 475 alias Pair = typeof(V[K].init.pairs[0]); 476 Pair[] result; 477 result.reserve(length); 478 foreach (ref k, ref v; this) 479 result ~= Pair(k, v); 480 return result; 481 } 482 483 @property size_t length() const { return items.byValue.map!(item => item.length).sum(); } 484 485 auto byKey() { return items.byKey(); } 486 auto byValue() { return items.byValue().joiner(); } 487 488 bool opCast(T)() inout 489 if (is(T == bool)) 490 { 491 return !!items; 492 } 493 494 /// Warning: discards repeating items 495 V[K] opCast(T)() const 496 if (is(T == V[K])) 497 { 498 V[K] result; 499 foreach (key, value; this) 500 result[key] = value; 501 return result; 502 } 503 504 V[][K] opCast(T)() inout 505 if (is(T == V[][K])) 506 { 507 V[][K] result; 508 foreach (k, v; this) 509 result[k] ~= v; 510 return result; 511 } 512 } 513 514 unittest 515 { 516 MultiAA!(string, string) aa; 517 }