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 static if (__traits(hasMember, object, "require")) 43 return aa.require(key, defaultValue); 44 else 45 { 46 auto p = key in aa; 47 if (!p) 48 { 49 aa[key] = defaultValue; 50 p = key in aa; 51 } 52 return *p; 53 } 54 } 55 56 unittest 57 { 58 int[int] aa; 59 aa.getOrAdd(1, 2) = 3; 60 assert(aa[1] == 3); 61 assert(aa.getOrAdd(1, 4) == 3); 62 } 63 64 /// If key is not in aa, add it with the given value, and return true. 65 /// Otherwise, return false. 66 bool addNew(K, V)(ref V[K] aa, auto ref K key, auto ref V value) 67 { 68 static if (__traits(hasMember, object, "update")) 69 { 70 bool added = void; 71 aa.update(key, 72 delegate V( ) { added = true ; return value; }, 73 delegate V(ref V v) { added = false; return v ; }, 74 ); 75 return added; 76 } 77 else 78 { 79 auto p = key in aa; 80 if (!p) 81 { 82 aa[key] = value; 83 return true; 84 } 85 else 86 return false; 87 } 88 } 89 90 unittest 91 { 92 int[int] aa; 93 assert( aa.addNew(1, 2)); 94 assert(!aa.addNew(1, 3)); 95 assert(aa[1] == 2); 96 } 97 98 struct KeyValuePair(K, V) { K key; V value; } 99 100 /// Get key/value pairs from AA 101 KeyValuePair!(K, V)[] pairs(K, V)(V[K] aa) 102 { 103 KeyValuePair!(K, V)[] result; 104 foreach (key, value; aa) 105 result ~= KeyValuePair!(K, V)(key, value); 106 return result; 107 } 108 109 /// Get key/value pairs from AA, sorted by keys 110 KeyValuePair!(K, V)[] sortedPairs(K, V)(V[K] aa) 111 { 112 KeyValuePair!(K, V)[] result; 113 foreach (key; aa.keys.sort) 114 result ~= KeyValuePair!(K, V)(key, aa[key]); 115 return result; 116 } 117 118 /// Get values from AA, sorted by keys 119 V[] sortedValues(K, V)(in V[K] aa) 120 { 121 V[] result; 122 foreach (key; aa.keys.sort()) 123 result ~= aa[key]; 124 return result; 125 } 126 127 /// Merge source into target. Return target. 128 V[K] merge(K, V)(auto ref V[K] target, in V[K] source) 129 { 130 foreach (k, v; source) 131 target[k] = v; 132 return target; 133 } 134 135 unittest 136 { 137 int[int] target; 138 int[int] source = [2:4]; 139 merge(target, source); 140 assert(source == target); 141 142 target = [1:1, 2:2, 3:3]; 143 merge(target, source); 144 assert(target == [1:1, 2:4, 3:3]); 145 146 assert(merge([1:1], [2:2]) == [1:1, 2:2]); 147 } 148 149 /// Slurp a range of two elements (or two-element struct/class) into an AA. 150 auto toAA(R)(R r) 151 if (is(typeof(r.front[1]))) 152 { 153 alias K = typeof(r.front[0]); 154 alias V = typeof(r.front[1]); 155 V[K] result; 156 foreach (pair; r) 157 { 158 assert(pair.length == 2); 159 result[pair[0]] = pair[1]; 160 } 161 return result; 162 } 163 164 /// ditto 165 auto toAA(R)(R r) 166 if (is(typeof(r.front.tupleof)) && r.front.tupleof.length == 2 && !is(typeof(r.front[1]))) 167 { 168 return r.map!(el => tuple(el.tupleof)).toAA(); 169 } 170 171 unittest 172 { 173 assert([[2, 4]].toAA() == [2:4]); 174 assert([2:4].pairs.toAA() == [2:4]); 175 } 176 177 // *************************************************************************** 178 179 /// An associative array which retains the order in which elements were added. 180 struct OrderedMap(K, V) 181 { 182 K[] keys; 183 V[] values; 184 size_t[K] index; 185 186 /// Convert from regular AA 187 this(V[K] aa) 188 { 189 opAssign(aa); 190 } 191 192 static if (is(typeof(keys.dup && values.dup && index.dup))) 193 { 194 this(this) 195 { 196 keys = keys.dup; 197 values = values.dup; 198 index = index.dup; 199 } 200 } 201 else 202 @disable this(this); 203 204 void opAssign(V[K] aa) 205 { 206 clear(); 207 foreach (ref k, ref v; aa) 208 { 209 index[k] = values.length; 210 keys ~= k; 211 values ~= v; 212 } 213 } 214 215 void clear() 216 { 217 keys = null; 218 values = null; 219 index = null; 220 } 221 222 bool opCast(T)() const 223 if (is(T == bool)) 224 { 225 return !!index; 226 } 227 228 ref inout(V) opIndex()(auto ref K k) inout 229 { 230 return values[index[k]]; 231 } 232 233 ref V opIndexAssign()(auto ref V v, auto ref K k) 234 { 235 auto pi = k in index; 236 if (pi) 237 { 238 auto pv = &values[*pi]; 239 *pv = v; 240 return *pv; 241 } 242 243 index[k] = values.length; 244 keys ~= k; 245 values ~= v; 246 return values[$-1]; 247 } 248 249 ref V getOrAdd()(auto ref K k) 250 { 251 auto pi = k in index; 252 V* pv; 253 if (pi) 254 pv = &values[*pi]; 255 else 256 { 257 index[k] = values.length; 258 keys ~= k; 259 values ~= V.init; 260 pv = &values[$-1]; 261 } 262 return *pv; 263 } 264 265 ref V opIndexUnary(string op)(auto ref K k) 266 { 267 auto pv = &getOrAdd(k); 268 mixin("(*pv) " ~ op ~ ";"); 269 return *pv; 270 } 271 272 ref V opIndexOpAssign(string op)(auto ref V v, auto ref K k) 273 { 274 auto pv = &getOrAdd(k); 275 mixin("(*pv) " ~ op ~ "= v;"); 276 return *pv; 277 } 278 279 inout(V) get()(auto ref K k, inout(V) defaultValue) inout 280 { 281 auto p = k in index; 282 return p ? values[*p] : defaultValue; 283 } 284 285 inout(V)* opBinaryRight(string op)(auto ref in K k) inout 286 if (op == "in") 287 { 288 auto p = k in index; 289 return p ? &values[*p] : null; 290 } 291 292 void remove()(auto ref K k) 293 { 294 auto i = index[k]; 295 index.remove(k); 296 keys = keys.remove(i); 297 values = values.remove(i); 298 foreach (key, ref idx; index) 299 if (idx > i) 300 idx--; 301 } 302 303 @property size_t length() const { return values.length; } 304 305 private int opApplyImpl(this This, Dg)(Dg dg) 306 { 307 int result = 0; 308 309 foreach (i, ref v; values) 310 { 311 result = dg(keys[i], v); 312 if (result) 313 break; 314 } 315 return result; 316 } 317 318 int opApply(int delegate(ref K k, ref V v) dg) 319 { 320 return opApplyImpl(dg); 321 } 322 323 int opApply(int delegate(const ref K k, const ref V v) dg) const 324 { 325 return opApplyImpl(dg); 326 } 327 328 @property typeof(this) dup() 329 { 330 typeof(this) result; 331 result.keys = keys.dup; 332 result.values = values.dup; 333 result.index = index.dup; 334 return result; 335 } 336 337 alias byKey = keys; 338 alias byValue = values; 339 340 auto byKeyValue(this T)() 341 { 342 auto instance = this; 343 struct Range 344 { 345 size_t index; 346 bool empty() const { return index == instance.values.length; } 347 static struct KeyValue { typeof(instance.keys[0]) key; typeof(instance.values[0]) value; } 348 KeyValue front() { return KeyValue(instance.keys[index], instance.values[index]); } 349 void popFront() { index++; } 350 } 351 return Range(); 352 } 353 } 354 355 unittest 356 { 357 OrderedMap!(string, int) m; 358 m["a"] = 1; 359 m["b"] = 2; 360 m["c"] = 3; 361 assert(m.length == 3); 362 assert("a" in m); 363 assert("d" !in m); 364 365 { 366 auto r = m.byKeyValue; 367 assert(!r.empty); 368 assert(r.front.key == "a"); 369 r.popFront(); 370 assert(!r.empty); 371 assert(r.front.key == "b"); 372 r.popFront(); 373 assert(!r.empty); 374 assert(r.front.key == "c"); 375 r.popFront(); 376 assert(r.empty); 377 } 378 379 m.remove("a"); 380 assert(m.length == 2); 381 m["x"] -= 1; 382 assert(m["x"] == -1); 383 ++m["y"]; 384 assert(m["y"] == 1); 385 auto cm = cast(const)m.dup; 386 foreach (k, v; cm) 387 if (k == "x") 388 assert(v == -1); 389 } 390 391 unittest 392 { 393 OrderedMap!(string, int) m; 394 m["a"] = 1; 395 m["b"] = 2; 396 m.remove("a"); 397 assert(m["b"] == 2); 398 } 399 400 unittest 401 { 402 OrderedMap!(string, int) m; 403 m["a"] = 1; 404 auto m2 = m; 405 m2.remove("a"); 406 m2["b"] = 2; 407 assert(m["a"] == 1); 408 } 409 410 unittest 411 { 412 OrderedMap!(string, int) m; 413 m["a"] = 1; 414 m["b"] = 2; 415 auto m2 = m; 416 m.remove("a"); 417 assert(m2["a"] == 1); 418 } 419 420 unittest 421 { 422 class C {} 423 const OrderedMap!(string, C) m; 424 m.byKeyValue; 425 } 426 427 // https://issues.dlang.org/show_bug.cgi?id=18606 428 unittest 429 { 430 struct S 431 { 432 struct T 433 { 434 int foo; 435 int[] bar; 436 } 437 438 OrderedMap!(int, T) m; 439 } 440 } 441 442 // *************************************************************************** 443 444 /// Helper/wrapper for void[0][T] 445 struct HashSet(T) 446 { 447 void[0][T] data; 448 449 alias data this; 450 451 this(R)(R r) 452 { 453 foreach (k; r) 454 add(k); 455 } 456 457 void add(T k) 458 { 459 void[0] v; 460 data[k] = v; 461 } 462 463 void remove(T k) 464 { 465 data.remove(k); 466 } 467 468 @property HashSet!T dup() const 469 { 470 // Can't use .dup with void[0] value 471 HashSet!T result; 472 foreach (k, v; data) 473 result.add(k); 474 return result; 475 } 476 477 int opApply(scope int delegate(ref T) dg) 478 { 479 int result; 480 foreach (k, v; data) 481 if ((result = dg(k)) != 0) 482 break; 483 return result; 484 } 485 } 486 487 unittest 488 { 489 HashSet!int s; 490 assert(s.length == 0); 491 assert(!(1 in s)); 492 assert(1 !in s); 493 s.add(1); 494 assert(1 in s); 495 assert(s.length == 1); 496 foreach (k; s) 497 assert(k == 1); 498 s.remove(1); 499 assert(s.length == 0); 500 501 s.add(1); 502 auto t = s.dup; 503 s.add(2); 504 assert(t.length==1); 505 t.remove(1); 506 assert(t.length==0); 507 } 508 509 auto toSet(R)(R r) 510 { 511 alias E = ElementType!R; 512 return HashSet!E(r); 513 } 514 515 unittest 516 { 517 auto set = [1, 2, 3].toSet(); 518 assert(2 in set); 519 assert(4 !in set); 520 } 521 522 // *************************************************************************** 523 524 struct OrderedSet(T) 525 { 526 T[] items; 527 size_t[T] index; 528 529 this(R)(R r) 530 if (isInputRange!R) 531 { 532 foreach (k; r) 533 add(k); 534 } 535 536 static if (is(typeof(items.dup && index.dup))) 537 { 538 this(this) 539 { 540 items = items.dup; 541 index = index.dup; 542 } 543 } 544 else 545 @disable this(this); 546 547 void clear() 548 { 549 items = null; 550 index = null; 551 } 552 553 bool opCast(T)() const 554 if (is(T == bool)) 555 { 556 return !!index; 557 } 558 559 ref inout(T) opIndex()(size_t i) inout 560 { 561 return items[i]; 562 } 563 564 ref T opIndexAssign()(auto ref T v, size_t i) 565 { 566 assert(i < items.length); 567 index.remove(items[i]); 568 items[i] = v; 569 index[v] = i; 570 return items[i]; 571 } 572 573 bool opBinaryRight(string op)(auto ref in T v) inout 574 if (op == "in") 575 { 576 return !!(v in index); 577 } 578 579 ref T add()(auto ref T v) 580 { 581 auto pi = v in index; 582 if (pi) 583 { 584 auto pv = &items[*pi]; 585 *pv = v; 586 return *pv; 587 } 588 589 index[v] = items.length; 590 items ~= v; 591 return items[$-1]; 592 } 593 594 void remove()(auto ref T v) 595 { 596 auto i = index[v]; 597 index.remove(v); 598 items = items.remove(i); 599 foreach (key, ref idx; index) 600 if (idx > i) 601 idx--; 602 } 603 604 @property size_t length() const { return items.length; } 605 606 private int opApplyImpl(this This, Dg)(Dg dg) 607 { 608 int result = 0; 609 610 foreach (i, ref v; items) 611 { 612 result = dg(v); 613 if (result) 614 break; 615 } 616 return result; 617 } 618 619 int opApply(int delegate(ref T k) dg) 620 { 621 return opApplyImpl(dg); 622 } 623 624 int opApply(int delegate(const ref T k) dg) const 625 { 626 return opApplyImpl(dg); 627 } 628 629 @property typeof(this) dup() 630 { 631 typeof(this) result; 632 result.items = items.dup; 633 result.index = index.dup; 634 return result; 635 } 636 } 637 638 unittest 639 { 640 OrderedSet!int set; 641 642 assert(1 !in set); 643 set.add(1); 644 assert(1 in set); 645 set.remove(1); 646 assert(1 !in set); 647 648 set.add(1); 649 set.clear(); 650 assert(1 !in set); 651 652 set = set.init; 653 assert(!set); 654 set.add(1); 655 assert(!!set); 656 657 assert(set[0] == 1); 658 set[0] = 2; 659 assert(set[0] == 2); 660 assert(1 !in set); 661 assert(2 in set); 662 663 assert(set.length == 1); 664 set.remove(2); 665 assert(set.length == 0); 666 667 set.add(1); 668 auto set2 = set; 669 set.remove(1); 670 set.add(2); 671 assert(1 !in set && 2 in set); 672 assert(1 in set2 && 2 !in set2); 673 674 foreach (v; set) 675 assert(v == 2); 676 } 677 678 // *************************************************************************** 679 680 /// An object which acts mostly as an associative array, 681 /// with the added property of being able to hold keys with 682 /// multiple values. These are only exposed explicitly and 683 /// through iteration 684 struct MultiAA(K, V) 685 { 686 V[][K] items; 687 688 /// If multiple items with this name are present, 689 /// only the first one is returned. 690 ref inout(V) opIndex(K key) inout 691 { 692 return items[key][0]; 693 } 694 695 V opIndexAssign(V value, K key) 696 { 697 items[key] = [value]; 698 return value; 699 } 700 701 inout(V)* opBinaryRight(string op)(K key) inout @nogc 702 if (op == "in") 703 { 704 auto pvalues = key in items; 705 if (pvalues && (*pvalues).length) 706 return &(*pvalues)[0]; 707 return null; 708 } 709 710 void remove(K key) 711 { 712 items.remove(key); 713 } 714 715 // D forces these to be "ref" 716 int opApply(int delegate(ref K key, ref V value) dg) 717 { 718 int ret; 719 outer: 720 foreach (key, values; items) 721 foreach (ref value; values) 722 { 723 ret = dg(key, value); 724 if (ret) 725 break outer; 726 } 727 return ret; 728 } 729 730 // Copy-paste because of https://issues.dlang.org/show_bug.cgi?id=7543 731 int opApply(int delegate(ref const(K) key, ref const(V) value) dg) const 732 { 733 int ret; 734 outer: 735 foreach (key, values; items) 736 foreach (ref value; values) 737 { 738 ret = dg(key, value); 739 if (ret) 740 break outer; 741 } 742 return ret; 743 } 744 745 void add(K key, V value) 746 { 747 if (key !in items) 748 items[key] = [value]; 749 else 750 items[key] ~= value; 751 } 752 753 V get(K key, lazy V def) const 754 { 755 auto pvalue = key in this; 756 return pvalue ? *pvalue : def; 757 } 758 759 inout(V)[] getAll(K key) inout 760 { 761 inout(V)[] result; 762 foreach (ref value; items.get(key, null)) 763 result ~= value; 764 return result; 765 } 766 767 this(typeof(null) Null) 768 { 769 } 770 771 this(V[K] aa) 772 { 773 foreach (ref key, ref value; aa) 774 add(key, value); 775 } 776 777 this(V[][K] aa) 778 { 779 foreach (ref key, values; aa) 780 foreach (ref value; values) 781 add(key, value); 782 } 783 784 @property auto keys() inout { return items.keys; } 785 786 // https://issues.dlang.org/show_bug.cgi?id=14626 787 788 @property V[] values() 789 { 790 return items.byValue.join; 791 } 792 793 @property const(V)[] values() const 794 { 795 return items.byValue.join; 796 } 797 798 @property typeof(V[K].init.pairs) pairs() 799 { 800 alias Pair = typeof(V[K].init.pairs[0]); 801 Pair[] result; 802 result.reserve(length); 803 foreach (ref k, ref v; this) 804 result ~= Pair(k, v); 805 return result; 806 } 807 808 @property size_t length() const { return items.byValue.map!(item => item.length).sum(); } 809 810 auto byKey() { return items.byKey(); } 811 auto byValue() { return items.byValue().joiner(); } 812 813 bool opCast(T)() inout 814 if (is(T == bool)) 815 { 816 return !!items; 817 } 818 819 /// Warning: discards repeating items 820 V[K] opCast(T)() const 821 if (is(T == V[K])) 822 { 823 V[K] result; 824 foreach (key, value; this) 825 result[key] = value; 826 return result; 827 } 828 829 V[][K] opCast(T)() inout 830 if (is(T == V[][K])) 831 { 832 V[][K] result; 833 foreach (k, v; this) 834 result[k] ~= v; 835 return result; 836 } 837 } 838 839 unittest 840 { 841 MultiAA!(string, string) aa; 842 }