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