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)(auto ref 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 /// Convert from regular AA 148 this(V[K] aa) 149 { 150 opAssign(aa); 151 } 152 153 static if (is(typeof(keys.dup && values.dup && index.dup))) 154 { 155 this(this) 156 { 157 keys = keys.dup; 158 values = values.dup; 159 index = index.dup; 160 } 161 } 162 else 163 @disable this(this); 164 165 void opAssign(V[K] aa) 166 { 167 clear(); 168 foreach (ref k, ref v; aa) 169 { 170 index[k] = values.length; 171 keys ~= k; 172 values ~= v; 173 } 174 } 175 176 void clear() 177 { 178 keys = null; 179 values = null; 180 index = null; 181 } 182 183 ref inout(V) opIndex()(auto ref K k) inout 184 { 185 return values[index[k]]; 186 } 187 188 ref V opIndexAssign()(auto ref V v, auto ref K k) 189 { 190 auto pi = k in index; 191 if (pi) 192 { 193 auto pv = &values[*pi]; 194 *pv = v; 195 return *pv; 196 } 197 198 index[k] = values.length; 199 keys ~= k; 200 values ~= v; 201 return values[$-1]; 202 } 203 204 ref V getOrAdd()(auto ref K k) 205 { 206 auto pi = k in index; 207 V* pv; 208 if (pi) 209 pv = &values[*pi]; 210 else 211 { 212 index[k] = values.length; 213 keys ~= k; 214 values ~= V.init; 215 pv = &values[$-1]; 216 } 217 return *pv; 218 } 219 220 ref V opIndexUnary(string op)(auto ref K k) 221 { 222 auto pv = &getOrAdd(k); 223 mixin("(*pv) " ~ op ~ ";"); 224 return *pv; 225 } 226 227 ref V opIndexOpAssign(string op)(auto ref V v, auto ref K k) 228 { 229 auto pv = &getOrAdd(k); 230 mixin("(*pv) " ~ op ~ "= v;"); 231 return *pv; 232 } 233 234 inout(V) get()(auto ref K k, inout(V) defaultValue) inout 235 { 236 auto p = k in index; 237 return p ? values[*p] : defaultValue; 238 } 239 240 inout(V)* opIn_r()(auto ref K k) inout 241 { 242 auto p = k in index; 243 return p ? &values[*p] : null; 244 } 245 246 void remove()(auto ref K k) 247 { 248 auto i = index[k]; 249 index.remove(k); 250 keys = keys.remove(i); 251 values = values.remove(i); 252 foreach (key, ref idx; index) 253 if (idx > i) 254 idx--; 255 } 256 257 @property size_t length() const { return values.length; } 258 259 private int opApplyImpl(this This, Dg)(Dg dg) 260 { 261 int result = 0; 262 263 foreach (i, ref v; values) 264 { 265 result = dg(keys[i], v); 266 if (result) 267 break; 268 } 269 return result; 270 } 271 272 int opApply(int delegate(ref K k, ref V v) dg) 273 { 274 return opApplyImpl(dg); 275 } 276 277 int opApply(int delegate(const ref K k, const ref V v) dg) const 278 { 279 return opApplyImpl(dg); 280 } 281 282 @property typeof(this) dup() 283 { 284 typeof(this) result; 285 result.keys = keys.dup; 286 result.values = values.dup; 287 result.index = index.dup; 288 return result; 289 } 290 } 291 292 unittest 293 { 294 OrderedMap!(string, int) m; 295 m["a"] = 1; 296 m["b"] = 2; 297 m["c"] = 3; 298 assert(m.length == 3); 299 assert("a" in m); 300 assert("d" !in m); 301 m.remove("a"); 302 assert(m.length == 2); 303 m["x"] -= 1; 304 assert(m["x"] == -1); 305 ++m["y"]; 306 assert(m["y"] == 1); 307 auto cm = cast(const)m.dup; 308 foreach (k, v; cm) 309 if (k == "x") 310 assert(v == -1); 311 } 312 313 unittest 314 { 315 OrderedMap!(string, int) m; 316 m["a"] = 1; 317 m["b"] = 2; 318 m.remove("a"); 319 assert(m["b"] == 2); 320 } 321 322 unittest 323 { 324 OrderedMap!(string, int) m; 325 m["a"] = 1; 326 auto m2 = m; 327 m2.remove("a"); 328 m2["b"] = 2; 329 assert(m["a"] == 1); 330 } 331 332 unittest 333 { 334 OrderedMap!(string, int) m; 335 m["a"] = 1; 336 m["b"] = 2; 337 auto m2 = m; 338 m.remove("a"); 339 assert(m2["a"] == 1); 340 } 341 342 // https://issues.dlang.org/show_bug.cgi?id=18606 343 unittest 344 { 345 struct S 346 { 347 struct T 348 { 349 int foo; 350 int[] bar; 351 } 352 353 OrderedMap!(int, T) m; 354 } 355 } 356 357 // *************************************************************************** 358 359 /// Helper/wrapper for void[0][T] 360 struct HashSet(T) 361 { 362 void[0][T] data; 363 364 alias data this; 365 366 this(R)(R r) 367 { 368 foreach (k; r) 369 add(k); 370 } 371 372 void add(T k) 373 { 374 void[0] v; 375 data[k] = v; 376 } 377 378 void remove(T k) 379 { 380 data.remove(k); 381 } 382 383 @property HashSet!T dup() const 384 { 385 // Can't use .dup with void[0] value 386 HashSet!T result; 387 foreach (k, v; data) 388 result.add(k); 389 return result; 390 } 391 392 int opApply(scope int delegate(ref T) dg) 393 { 394 int result; 395 foreach (k, v; data) 396 if ((result = dg(k)) != 0) 397 break; 398 return result; 399 } 400 } 401 402 unittest 403 { 404 HashSet!int s; 405 assert(s.length == 0); 406 assert(!(1 in s)); 407 assert(1 !in s); 408 s.add(1); 409 assert(1 in s); 410 assert(s.length == 1); 411 foreach (k; s) 412 assert(k == 1); 413 s.remove(1); 414 assert(s.length == 0); 415 416 s.add(1); 417 auto t = s.dup; 418 s.add(2); 419 assert(t.length==1); 420 t.remove(1); 421 assert(t.length==0); 422 } 423 424 auto toSet(R)(R r) 425 { 426 alias E = ElementType!R; 427 return HashSet!E(r); 428 } 429 430 unittest 431 { 432 auto set = [1, 2, 3].toSet(); 433 assert(2 in set); 434 assert(4 !in set); 435 } 436 437 // *************************************************************************** 438 439 /// An object which acts mostly as an associative array, 440 /// with the added property of being able to hold keys with 441 /// multiple values. These are only exposed explicitly and 442 /// through iteration 443 struct MultiAA(K, V) 444 { 445 V[][K] items; 446 447 /// If multiple items with this name are present, 448 /// only the first one is returned. 449 ref inout(V) opIndex(K key) inout 450 { 451 return items[key][0]; 452 } 453 454 V opIndexAssign(V value, K key) 455 { 456 items[key] = [value]; 457 return value; 458 } 459 460 inout(V)* opIn_r(K key) inout @nogc 461 { 462 auto pvalues = key in items; 463 if (pvalues && (*pvalues).length) 464 return &(*pvalues)[0]; 465 return null; 466 } 467 468 void remove(K key) 469 { 470 items.remove(key); 471 } 472 473 // D forces these to be "ref" 474 int opApply(int delegate(ref K key, ref V value) dg) 475 { 476 int ret; 477 outer: 478 foreach (key, values; items) 479 foreach (ref value; values) 480 { 481 ret = dg(key, value); 482 if (ret) 483 break outer; 484 } 485 return ret; 486 } 487 488 // Copy-paste because of https://issues.dlang.org/show_bug.cgi?id=7543 489 int opApply(int delegate(ref const(K) key, ref const(V) value) dg) const 490 { 491 int ret; 492 outer: 493 foreach (key, values; items) 494 foreach (ref value; values) 495 { 496 ret = dg(key, value); 497 if (ret) 498 break outer; 499 } 500 return ret; 501 } 502 503 void add(K key, V value) 504 { 505 if (key !in items) 506 items[key] = [value]; 507 else 508 items[key] ~= value; 509 } 510 511 V get(K key, lazy V def) const 512 { 513 auto pvalue = key in this; 514 return pvalue ? *pvalue : def; 515 } 516 517 inout(V)[] getAll(K key) inout 518 { 519 inout(V)[] result; 520 foreach (ref value; items.get(key, null)) 521 result ~= value; 522 return result; 523 } 524 525 this(typeof(null) Null) 526 { 527 } 528 529 this(V[K] aa) 530 { 531 foreach (ref key, ref value; aa) 532 add(key, value); 533 } 534 535 this(V[][K] aa) 536 { 537 foreach (ref key, values; aa) 538 foreach (ref value; values) 539 add(key, value); 540 } 541 542 @property auto keys() inout { return items.keys; } 543 544 // https://issues.dlang.org/show_bug.cgi?id=14626 545 546 @property V[] values() 547 { 548 return items.byValue.join; 549 } 550 551 @property const(V)[] values() const 552 { 553 return items.byValue.join; 554 } 555 556 @property typeof(V[K].init.pairs) pairs() 557 { 558 alias Pair = typeof(V[K].init.pairs[0]); 559 Pair[] result; 560 result.reserve(length); 561 foreach (ref k, ref v; this) 562 result ~= Pair(k, v); 563 return result; 564 } 565 566 @property size_t length() const { return items.byValue.map!(item => item.length).sum(); } 567 568 auto byKey() { return items.byKey(); } 569 auto byValue() { return items.byValue().joiner(); } 570 571 bool opCast(T)() inout 572 if (is(T == bool)) 573 { 574 return !!items; 575 } 576 577 /// Warning: discards repeating items 578 V[K] opCast(T)() const 579 if (is(T == V[K])) 580 { 581 V[K] result; 582 foreach (key, value; this) 583 result[key] = value; 584 return result; 585 } 586 587 V[][K] opCast(T)() inout 588 if (is(T == V[][K])) 589 { 590 V[][K] result; 591 foreach (k, v; this) 592 result[k] ~= v; 593 return result; 594 } 595 } 596 597 unittest 598 { 599 MultiAA!(string, string) aa; 600 }