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.traits; 19 import std.typecons; 20 21 // *************************************************************************** 22 23 /// Polyfill for object.require 24 static if (!__traits(hasMember, object, "require")) 25 ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init) 26 { 27 auto p = key in aa; 28 if (p) 29 return *p; 30 return aa[key] = value; 31 } 32 33 unittest 34 { 35 int[int] aa; 36 aa.require(1, 2); 37 assert(aa[1] == 2); 38 aa.require(2, 3) = 4; 39 assert(aa[2] == 4); 40 aa.require(1, 5); 41 assert(aa[1] == 2); 42 aa.require(1, 6) = 7; 43 assert(aa[1] == 7); 44 } 45 46 static if (!__traits(hasMember, object, "update")) 47 { 48 /// Polyfill for object.update 49 void updatePolyfill(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update) 50 if (is(typeof(create()) : V) && is(typeof(update(aa[K.init])) : V)) 51 { 52 auto p = key in aa; 53 if (p) 54 *p = update(*p); 55 else 56 aa[key] = create(); 57 } 58 59 /// Work around https://issues.dlang.org/show_bug.cgi?id=15795 60 alias update = updatePolyfill; 61 } 62 63 // https://github.com/dlang/druntime/pull/3012 64 private enum haveObjectUpdateWithVoidUpdate = is(typeof({ 65 int[int] aa; 66 .object.update(aa, 0, { return 0; }, (ref int v) { }); 67 })); 68 69 static if (!haveObjectUpdateWithVoidUpdate) 70 { 71 /// Polyfill for object.update with void update function 72 void updateVoid(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update) 73 if (is(typeof(create()) : V) && is(typeof(update(aa[K.init])) == void)) 74 { 75 // We can polyfill this in two ways. 76 // What's more expensive, copying the value, or a second key lookup? 77 enum haveObjectUpdate = __traits(hasMember, object, "update"); 78 enum valueIsExpensiveToCopy = V.sizeof > string.sizeof 79 || hasElaborateCopyConstructor!V 80 || hasElaborateDestructor!V; 81 static if (haveObjectUpdate && !valueIsExpensiveToCopy) 82 { 83 .object.update(aa, key, create, 84 (ref V v) { update(v); return v; }); 85 } 86 else 87 { 88 auto p = key in aa; 89 if (p) 90 update(*p); 91 else 92 aa[key] = create(); 93 } 94 } 95 96 /// Work around https://issues.dlang.org/show_bug.cgi?id=15795 97 alias update = updateVoid; 98 } 99 else 100 alias updateVoid = object.update; 101 102 // Inject overload 103 static if (__traits(hasMember, object, "update")) 104 private alias update = object.update; 105 106 // *************************************************************************** 107 108 /// Get a value from an AA, and throw an exception (not an error) if not found 109 ref auto aaGet(AA, K)(auto ref AA aa, auto ref K key) 110 if (is(typeof(key in aa))) 111 { 112 import std.conv; 113 114 auto p = key in aa; 115 if (p) 116 return *p; 117 else 118 static if (is(typeof(text(key)))) 119 throw new Exception("Absent value: " ~ text(key)); 120 else 121 throw new Exception("Absent value"); 122 } 123 124 /// If key is not in aa, add it with defaultValue. 125 /// Returns a reference to the value corresponding to key. 126 ref V getOrAdd(K, V)(ref V[K] aa, auto ref K key, auto ref V defaultValue) 127 { 128 return aa.require(key, defaultValue); 129 } 130 131 /// ditto 132 ref V getOrAdd(K, V)(ref V[K] aa, auto ref K key) 133 { 134 return getOrAdd(aa, key, V.init); 135 } 136 137 unittest 138 { 139 int[int] aa; 140 aa.getOrAdd(1, 2) = 3; 141 assert(aa[1] == 3); 142 assert(aa.getOrAdd(1, 4) == 3); 143 } 144 145 /// If key is not in aa, add it with the given value, and return true. 146 /// Otherwise, return false. 147 bool addNew(K, V)(ref V[K] aa, auto ref K key, auto ref V value) 148 { 149 bool added = void; 150 updateVoid(aa, key, 151 delegate V ( ) { added = true ; return value; }, 152 delegate void(ref V v) { added = false; }, 153 ); 154 return added; 155 } 156 157 unittest 158 { 159 int[int] aa; 160 assert( aa.addNew(1, 2)); 161 assert(!aa.addNew(1, 3)); 162 assert(aa[1] == 2); 163 } 164 165 // *************************************************************************** 166 167 struct KeyValuePair(K, V) { K key; V value; } 168 169 /// Get key/value pairs from AA 170 deprecated KeyValuePair!(K, V)[] pairs(K, V)(V[K] aa) 171 { 172 KeyValuePair!(K, V)[] result; 173 foreach (key, value; aa) 174 result ~= KeyValuePair!(K, V)(key, value); 175 return result; 176 } 177 178 /// Get key/value pairs from AA, sorted by keys 179 KeyValuePair!(K, V)[] sortedPairs(K, V)(V[K] aa) 180 { 181 KeyValuePair!(K, V)[] result; 182 foreach (key; aa.keys.sort) 183 result ~= KeyValuePair!(K, V)(key, aa[key]); 184 return result; 185 } 186 187 /// Get values from AA, sorted by keys 188 V[] sortedValues(K, V)(in V[K] aa) 189 { 190 V[] result; 191 foreach (key; aa.keys.sort()) 192 result ~= aa[key]; 193 return result; 194 } 195 196 /// Merge source into target. Return target. 197 V[K] merge(K, V)(auto ref V[K] target, in V[K] source) 198 { 199 foreach (k, v; source) 200 target[k] = v; 201 return target; 202 } 203 204 unittest 205 { 206 int[int] target; 207 int[int] source = [2:4]; 208 merge(target, source); 209 assert(source == target); 210 211 target = [1:1, 2:2, 3:3]; 212 merge(target, source); 213 assert(target == [1:1, 2:4, 3:3]); 214 215 assert(merge([1:1], [2:2]) == [1:1, 2:2]); 216 } 217 218 /// Slurp a range of two elements (or two-element struct/class) into an AA. 219 auto toAA(R)(R r) 220 if (is(typeof(r.front[1]))) 221 { 222 alias K = typeof(r.front[0]); 223 alias V = typeof(r.front[1]); 224 V[K] result; 225 foreach (pair; r) 226 { 227 assert(pair.length == 2); 228 result[pair[0]] = pair[1]; 229 } 230 return result; 231 } 232 233 /// ditto 234 auto toAA(R)(R r) 235 if (is(typeof(r.front.tupleof)) && r.front.tupleof.length == 2 && !is(typeof(r.front[1]))) 236 { 237 return r.map!(el => tuple(el.tupleof)).toAA(); 238 } 239 240 deprecated unittest 241 { 242 assert([[2, 4]].toAA() == [2:4]); 243 assert([2:4].pairs.toAA() == [2:4]); 244 } 245 246 /// Ensure that arr is non-null if empty. 247 V[K] nonNull(K, V)(V[K] aa) 248 { 249 if (aa !is null) 250 return aa; 251 aa[K.init] = V.init; 252 aa.remove(K.init); 253 assert(aa !is null); 254 return aa; 255 } 256 257 unittest 258 { 259 int[int] aa; 260 assert(aa is null); 261 aa = aa.nonNull; 262 assert(aa !is null); 263 assert(aa.length == 0); 264 } 265 266 // *************************************************************************** 267 268 // Helpers for HashCollection 269 private 270 { 271 alias Void = void[0]; // Zero-sized type 272 static assert(Void.sizeof == 0); 273 274 // Abstraction layer for single/multi-value type holding one or many T. 275 // Optimizer representation for Void. 276 struct SingleOrMultiValue(bool multi, T) 277 { 278 alias ValueType = Select!(multi, 279 // multi==true 280 Select!(is(T == Void), 281 size_t, // "store" the items by keeping track of their count only. 282 T[], 283 ), 284 285 // multi==false 286 Select!(is(T == Void), 287 Void, 288 T[1], 289 ), 290 ); 291 292 // Using free functions instead of struct methods, 293 // as structs always have non-zero size. 294 static: 295 296 size_t length(ref const ValueType v) nothrow 297 { 298 static if (is(T == Void)) 299 static if (multi) 300 return v; // count 301 else 302 return 1; 303 else 304 return v.length; // static or dynamic array 305 } 306 } 307 } 308 309 /// Base type for ordered/unordered single-value/multi-value map/set 310 /*private*/ struct HashCollection(K, V, bool ordered, bool multi) 311 { 312 private: 313 enum bool haveValues = !is(V == void); // Not a set 314 315 // The type for values used when a value variable is needed 316 alias ValueVarType = Select!(haveValues, V, Void); 317 318 // The type of a single element of the values of `this.lookup`. 319 // When ordered==true, we use size_t (index into `this.items`). 320 alias LookupItem = Select!(ordered, size_t, ValueVarType); 321 322 // The type of the values of `this.lookup`. 323 alias SM = SingleOrMultiValue!(multi, LookupItem); 324 alias LookupValue = SM.ValueType; 325 326 static if (haveValues) 327 { 328 alias ReturnType(Fallback) = V; 329 alias SingleIterationType = V; 330 alias OpIndexKeyType = K; 331 alias OpIndexValueType = V; 332 } 333 else 334 { 335 alias SingleIterationType = const(K); 336 static if (ordered) 337 { 338 alias OpIndexKeyType = size_t; 339 alias OpIndexValueType = K; 340 alias ReturnType(Fallback) = K; 341 } 342 else 343 { 344 alias OpIndexKeyType = void; 345 alias OpIndexValueType = void; 346 alias ReturnType(Fallback) = Fallback; 347 } 348 } 349 enum haveReturnType = !is(ReturnType!void == void); 350 enum haveIndexing = haveValues || ordered; 351 352 alias IK = OpIndexKeyType; 353 alias IV = OpIndexValueType; 354 355 // The contract we try to follow is that adding/removing items in 356 // one copy of the object will not affect other copies. 357 // Therefore, when we have array fields, make sure they are dup'd 358 // on copy, so that we don't trample older copies' data. 359 enum bool needDupOnCopy = ordered; 360 361 static if (haveReturnType) 362 { 363 static if (ordered) 364 /* */ ref inout(ReturnType!void) lookupToReturnValue(in LookupItem lookupItem) inout { return items[lookupItem].returnValue; } 365 else 366 static ref inout(ReturnType!void) lookupToReturnValue(ref inout(LookupItem) lookupItem) { return lookupItem ; } 367 } 368 369 // *** Data *** 370 371 // This is used for all key hash lookups. 372 LookupValue[K] lookup; 373 374 static if (ordered) 375 { 376 struct Item 377 { 378 K key; 379 ValueVarType value; 380 381 static if (haveValues) 382 private alias returnValue = value; 383 else 384 private alias returnValue = key; 385 } 386 Item[] items; 387 388 enum bool canDup = is(typeof(lookup.dup)) && is(typeof(items.dup)); 389 } 390 else 391 { 392 enum bool canDup = is(typeof(lookup.dup)); 393 } 394 395 public: 396 397 // *** Lifetime *** 398 399 /// Postblit 400 static if (needDupOnCopy) 401 { 402 static if (canDup) 403 this(this) 404 { 405 lookup = lookup.dup; 406 items = items.dup; 407 } 408 else 409 @disable this(this); 410 } 411 412 /// Create shallow copy 413 static if (canDup) 414 typeof(this) dup() 415 { 416 static if (needDupOnCopy) 417 return this; 418 else 419 { 420 typeof(this) copy; 421 copy.lookup = lookup.dup; 422 static if (ordered) 423 copy.items = items.dup; 424 return copy; 425 } 426 } 427 428 // *** Conversions (from) *** 429 430 /// Construct from something else 431 this(Input)(Input input) 432 if (is(typeof(opAssign(input)))) 433 { 434 opAssign(input); 435 } 436 437 /// Null assignment 438 ref typeof(this) opAssign(typeof(null) _) 439 { 440 clear(); 441 return this; 442 } 443 444 /// Convert from an associative type 445 ref typeof(this) opAssign(AA)(AA aa) 446 if (haveValues 447 && !is(AA : typeof(this)) 448 && is(typeof({ foreach (ref k, ref v; aa) add(k, v); }))) 449 { 450 clear(); 451 foreach (ref k, ref v; aa) 452 add(k, v); 453 return this; 454 } 455 456 /// Convert from an associative type of multiple items 457 ref typeof(this) opAssign(AA)(AA aa) 458 if (haveValues 459 && multi 460 && !is(AA : typeof(this)) 461 && is(typeof({ foreach (ref k, ref vs; aa) foreach (ref v; vs) add(k, v); }))) 462 { 463 clear(); 464 foreach (ref k, ref vs; aa) 465 foreach (ref v; vs) 466 add(k, v); 467 return this; 468 } 469 470 /// Convert from a range of tuples 471 ref typeof(this) opAssign(R)(R input) 472 if (haveValues 473 && is(typeof({ foreach (ref pair; input) add(pair[0], pair[1]); })) 474 && !is(typeof({ foreach (ref k, ref v; input) add(k, v); })) 475 && is(typeof(input.front.length)) 476 && input.front.length == 2) 477 { 478 clear(); 479 foreach (ref pair; input) 480 add(pair[0], pair[1]); 481 return this; 482 } 483 484 /// Convert from a range of key/value pairs 485 ref typeof(this) opAssign(R)(R input) 486 if (haveValues 487 && is(typeof({ foreach (ref pair; input) add(pair.key, pair.value); })) 488 && !is(typeof({ foreach (ref k, ref v; input) add(k, v); }))) 489 { 490 clear(); 491 foreach (ref pair; input) 492 add(pair.key, pair.value); 493 return this; 494 } 495 496 /// Convert from a range of values 497 ref typeof(this) opAssign(R)(R input) 498 if (!haveValues 499 && !is(R : typeof(this)) 500 && is(typeof({ foreach (ref v; input) add(v); }))) 501 { 502 clear(); 503 foreach (ref v; input) 504 add(v); 505 return this; 506 } 507 508 // *** Conversions (to) *** 509 510 /// Convert to bool (true if non-null) 511 bool opCast(T)() const 512 if (is(T == bool)) 513 { 514 return lookup !is null; 515 } 516 517 /// Convert to D associative array 518 static if (!ordered) 519 { 520 const(LookupValue[K]) toAA() const 521 { 522 return lookup; 523 } 524 525 LookupValue[K] toAA() 526 { 527 return lookup.dup; 528 } 529 530 deprecated alias items = toAA; 531 } 532 533 // *** Query (basic) *** 534 535 /// True when there are no items. 536 bool empty() pure const nothrow @nogc @trusted 537 { 538 static if (ordered) 539 return items.length == 0; // optimization 540 else 541 return lookup.byKey.empty; // generic version 542 } 543 544 /// Total number of items, including with duplicate keys. 545 size_t length() pure const nothrow @nogc @trusted 546 { 547 static if (ordered) 548 return items.length; // optimization 549 else 550 static if (!multi) 551 return lookup.length; // optimization 552 else // generic version 553 { 554 size_t result; 555 foreach (ref v; lookup.byValue) 556 result += SM.length(v); 557 return result; 558 } 559 } 560 561 // *** Query (by key) *** 562 563 /// Check if item with this key has been added. 564 /// When applicable, return a pointer to the last value added with this key. 565 Select!(haveReturnType, inout(ReturnType!void)*, bool) opBinaryRight(string op : "in", _K)(auto ref _K key) inout 566 if (is(typeof(key in lookup))) 567 { 568 enum missValue = select!haveReturnType(null, false); 569 570 auto p = key in lookup; 571 if (!p) 572 return missValue; 573 574 static if (haveReturnType) 575 return &lookupToReturnValue((*p)[$-1]); 576 else 577 return true; 578 } 579 580 /// Index operator. 581 /// The key must exist. Indexing with a key which does not exist 582 /// is an error. 583 static if (haveIndexing) 584 ref inout(IV) opIndex()(auto ref IK k) inout 585 { 586 static if (haveValues) 587 return lookupToReturnValue(lookup[k][$-1]); 588 else 589 return items[k].returnValue; 590 } 591 592 /// Retrieve last value associated with key, or `defaultValue` if none. 593 static if (haveIndexing) 594 auto ref inout(IV) get()(auto ref IK k, auto ref inout(IV) defaultValue) inout 595 { 596 static if (haveValues) 597 { 598 auto p = k in lookup; 599 return p ? lookupToReturnValue((*p)[$-1]) : defaultValue; 600 } 601 else 602 return k < items.length ? items[k].returnValue : defaultValue; 603 } 604 605 // *** Query (ranges) *** 606 607 /// Return a range which iterates over key/value pairs. 608 static if (haveValues) 609 auto byKeyValue(this This)() 610 { 611 static if (ordered) 612 return items; 613 else 614 { 615 return lookup 616 .byKeyValue 617 .map!(pair => 618 pair 619 .value 620 .map!(value => KeyValuePair!(K, V)(pair.key, value)) 621 ) 622 .joiner; 623 } 624 } 625 626 /// ditto 627 static if (haveValues) 628 auto byPair(this This)() 629 { 630 return byKeyValue 631 .map!(pair => tuple!("key", "value")(pair.key, pair.value)); 632 } 633 634 /// Return a range which iterates over all keys. 635 /// Duplicate keys will occur several times in the range. 636 auto byKey(this This)() 637 { 638 static if (ordered) 639 { 640 static ref getKey(MItem)(ref MItem item) { return item.key; } 641 return items.map!getKey; 642 } 643 else 644 { 645 return lookup 646 .byKeyValue 647 .map!(pair => 648 pair.key.repeat(SM.length(pair.value)) 649 ) 650 .joiner; 651 } 652 } 653 654 /// Return a range which iterates over all values. 655 static if (haveValues) 656 auto byValue(this This)() 657 { 658 static if (ordered) 659 { 660 static ref getValue(MItem)(ref MItem item) { return item.value; } 661 return items.map!getValue; 662 } 663 else 664 { 665 return lookup 666 .byKeyValue 667 .map!(pair => 668 pair 669 .value 670 ) 671 .joiner; 672 } 673 } 674 675 @property auto keys(this This)() { return byKey.array; } 676 @property auto values(this This)() { return byValue.array; } 677 678 // *** Query (search by key) *** 679 680 static if (ordered) 681 { 682 size_t indexOf()(auto ref const K k) 683 { 684 auto p = k in lookup; 685 return p ? (*p)[0] : -1; 686 } 687 688 size_t[] indicesOf()(auto ref const K k) 689 { 690 auto p = k in lookup; 691 return p ? (*p)[] : null; 692 } 693 } 694 695 /// Return the number of items with the given key. 696 /// When multi==false, always returns 0 or 1. 697 size_t count()(auto ref K k) 698 { 699 static if (ordered) 700 return indicesOf(k).length; 701 else 702 { 703 auto p = k in lookup; 704 return p ? SM.length(*p) : 0; 705 } 706 } 707 708 /// Return a range with all values with the given key. 709 /// If the key is not present, returns an empty range. 710 static if (haveValues) 711 auto byValueOf(this This)(auto ref K k) 712 { 713 static if (ordered) 714 return indicesOf(k).map!(index => items[index].value); 715 else 716 return valuesOf(k); 717 } 718 719 /// Return an array with all values with the given key. 720 /// If the key is not present, returns an empty array. 721 static if (haveValues) 722 V[] valuesOf()(auto ref K k) 723 { 724 static if (ordered) 725 return byValueOf(k).array; 726 else 727 { 728 static if (multi) 729 return lookup.get(k, null); 730 else 731 { 732 auto p = k in lookup; 733 return p ? (*p)[] : null; 734 } 735 } 736 } 737 738 static if (haveValues) 739 deprecated alias getAll = valuesOf; 740 741 // *** Iteration *** 742 743 private int opApplyImpl(this This, Dg)(Dg dg) 744 { 745 enum single = arity!dg == 1; 746 747 int result = 0; 748 749 static if (ordered) 750 { 751 foreach (ref item; items) 752 { 753 static if (single) 754 result = dg(item.returnValue); 755 else 756 result = dg(item.key, item.value); 757 if (result) 758 break; 759 } 760 } 761 else 762 { 763 outer: 764 foreach (ref key, ref values; lookup) 765 static if (haveValues) 766 { 767 foreach (ref value; values) 768 { 769 static if (single) 770 result = dg(value); 771 else 772 result = dg(key, value); 773 if (result) 774 break outer; 775 } 776 } 777 else 778 { 779 foreach (iteration; 0 .. SM.length(values)) 780 { 781 static assert(single); 782 result = dg(key); 783 if (result) 784 break outer; 785 } 786 } 787 } 788 return result; 789 } 790 791 /// Iterate over keys (sets) / values (maps). 792 int opApply(int delegate(ref SingleIterationType x) dg) 793 { 794 return opApplyImpl(dg); 795 } 796 797 /// ditto 798 int opApply(int delegate(const ref SingleIterationType x) dg) const 799 { 800 return opApplyImpl(dg); 801 } 802 803 static if (haveValues) 804 { 805 /// Iterate over keys and values. 806 int opApply(int delegate(K k, ref V v) dg) 807 { 808 return opApplyImpl(dg); 809 } 810 811 /// ditto 812 int opApply(int delegate(K k, const ref V v) dg) const 813 { 814 return opApplyImpl(dg); 815 } 816 } 817 818 // *** Mutation (addition) *** 819 820 private enum AddMode 821 { 822 add, /// Always add value 823 replace, /// Replace all previous values 824 require, /// Only add value if it did not exist before 825 } 826 827 private ref ReturnType!void addImpl(AddMode mode, AK, GV)(ref AK key, scope GV getValue) 828 if (is(AK : K)) 829 { 830 static if (ordered) 831 { 832 size_t addedIndex; 833 834 static if (multi && mode == AddMode.add) 835 { 836 addedIndex = items.length; 837 lookup[key] ~= addedIndex; 838 items ~= Item(key, getValue()); 839 } 840 else 841 { 842 lookup.updateVoid(key, 843 delegate LookupValue() 844 { 845 addedIndex = items.length; 846 items ~= Item(key, getValue()); 847 return [addedIndex]; 848 }, 849 delegate void(ref LookupValue existingIndex) 850 { 851 addedIndex = existingIndex[0]; 852 static if (mode != AddMode.require) 853 { 854 static if (multi) 855 { 856 static assert(mode == AddMode.replace); 857 existingIndex = existingIndex[0 .. 1]; 858 } 859 items[addedIndex].value = getValue(); 860 } 861 }); 862 } 863 864 return items[addedIndex].returnValue; 865 } 866 else // ordered 867 { 868 static if (haveValues) 869 { 870 static if (mode == AddMode.require) 871 return (lookup.require(key, [getValue()]))[0]; 872 else 873 static if (multi && mode == AddMode.add) 874 return (lookup[key] ~= getValue())[$-1]; 875 else 876 return (lookup[key] = [getValue()])[0]; 877 } 878 else 879 { 880 static if (multi) 881 { 882 static if (mode == AddMode.require) 883 lookup.require(key, 1); 884 else 885 static if (mode == AddMode.add) 886 lookup[key]++; 887 else 888 lookup[key] = 1; 889 } 890 else 891 lookup[key] = LookupValue.init; 892 // This branch returns void, as there is no reasonable 893 // ref to an AA key that we can return here. 894 } 895 } 896 } 897 898 /*private*/ template addSetFunc(AddMode mode) 899 { 900 static if (haveValues) 901 { 902 ref ReturnType!void addSetFunc(AK, AV)(auto ref AK key, auto ref AV value) 903 if (is(AK : K) && is(AV : V)) 904 { 905 return addImpl!mode(key, () => value); 906 } 907 } 908 else 909 { 910 ref ReturnType!void addSetFunc(AK)(auto ref AK key) 911 if (is(AK : K)) 912 { 913 ValueVarType value; // void[0] 914 return addImpl!mode(key, () => value); 915 } 916 } 917 } 918 919 /// Add an item. 920 alias add = addSetFunc!(AddMode.add); 921 922 /// Ensure a key exists (with the given value). 923 /// When `multi==true`, replaces all previous entries with this key. 924 /// Otherwise, behaves identically to `add`. 925 alias set = addSetFunc!(AddMode.replace); 926 927 /// Add `value` only if `key` is not present. 928 static if (haveValues) 929 ref V require()(auto ref K key, lazy V value = V.init) 930 { 931 return addImpl!(AddMode.require)(key, () => value); 932 } 933 934 deprecated alias getOrAdd = require; 935 936 private alias UpdateFuncRT(U) = typeof({ U u = void; V v = void; return u(v); }()); 937 938 /// If `key` is present, call `update` for every value; 939 /// otherwise, add new value with `create`. 940 static if (haveValues) 941 void update(C, U)(auto ref K key, scope C create, scope U update) 942 if (is(typeof(create()) : V) && (is(UpdateFuncRT!U : V) || is(UpdateFuncRT!U == void))) 943 { 944 static if (ordered) 945 { 946 lookup.updateVoid(key, 947 delegate LookupValue() 948 { 949 auto addedIndex = items.length; 950 items ~= Item(key, create()); 951 return [addedIndex]; 952 }, 953 delegate void(ref LookupValue existingIndex) 954 { 955 foreach (i; existingIndex) 956 static if (is(UpdateFuncRT!U == void)) 957 update(items[i].value); 958 else 959 items[i].value = update(items[i].value); 960 }); 961 } 962 else // ordered 963 { 964 lookup.updateVoid(key, 965 delegate LookupValue () 966 { 967 return [create()]; 968 }, 969 delegate void (ref LookupValue values) 970 { 971 foreach (ref value; values) 972 static if (is(UpdateFuncRT!U == void)) 973 update(value); 974 else 975 value = update(value); 976 }); 977 } 978 } 979 980 // *** Mutation (editing) *** 981 982 static if (haveIndexing) 983 { 984 static if (haveValues) 985 { 986 /// Same as `set(k, v)`. 987 ref IV opIndexAssign(AK, AV)(auto ref AV v, auto ref AK k) 988 if (is(AK : K) && is(AV : V)) 989 { 990 return set(k, v); 991 } 992 993 /// Perform cumulative operation with value 994 /// (initialized with `.init` if the key does not exist). 995 ref IV opIndexOpAssign(string op, AK, AV)(auto ref AV v, auto ref AK k) 996 if (is(AK : K) && is(AV : V)) 997 { 998 auto pv = &require(k); 999 return mixin("(*pv) " ~ op ~ "= v"); 1000 } 1001 1002 /// Perform unary operation with value 1003 /// (initialized with `.init` if the key does not exist). 1004 ref IV opIndexUnary(string op, AK)(auto ref AK k) 1005 if (is(AK : K)) 1006 { 1007 auto pv = &require(k); 1008 mixin("(*pv) " ~ op ~ ";"); 1009 return *pv; 1010 } 1011 } 1012 else 1013 { 1014 private ref K editIndex(size_t index, scope void delegate(ref K) edit) 1015 { 1016 auto item = &items[index]; 1017 K oldKey = item.key; 1018 auto pOldIndices = oldKey in lookup; 1019 assert(pOldIndices); 1020 1021 edit(item.key); 1022 1023 // Add new value 1024 1025 lookup.updateVoid(item.key, 1026 delegate LookupValue() 1027 { 1028 // New value did not exist. 1029 if ((*pOldIndices).length == 1) 1030 { 1031 // Optimization - migrate the Indexes value 1032 assert((*pOldIndices)[0] == index); 1033 return *pOldIndices; 1034 } 1035 else 1036 return [index]; 1037 }, 1038 delegate void(ref LookupValue existingIndex) 1039 { 1040 // Value(s) with the new key already existed 1041 static if (multi) 1042 existingIndex ~= index; 1043 else 1044 assert(false, "Collision after in-place edit of a non-multi ordered set element"); 1045 }); 1046 1047 // Remove old value 1048 1049 if ((*pOldIndices).length == 1) 1050 lookup.remove(oldKey); 1051 else 1052 static if (multi) 1053 *pOldIndices = (*pOldIndices).remove!(i => i == index); 1054 else 1055 assert(false); // Should be unreachable (`if` above will always be true) 1056 1057 return item.key; 1058 } 1059 1060 /// Allows writing to ordered sets by index. 1061 /// The total number of elements never changes as a result 1062 /// of such an operation - a consequence of which is that 1063 /// if multi==false, changing the value to one that's 1064 /// already in the set is an error. 1065 ref IV opIndexAssign()(auto ref IV v, auto ref IK k) 1066 { 1067 static if (haveValues) 1068 return set(k, v); 1069 else 1070 return editIndex(k, (ref IV e) { e = v; }); 1071 } 1072 1073 /// Perform cumulative operation with value at index. 1074 ref IV opIndexOpAssign(string op)(auto ref VV v, auto ref IK k) 1075 { 1076 return editIndex(k, (ref IV e) { mixin("e " ~ op ~ "= v;"); }); 1077 } 1078 1079 /// Perform unary operation with value at index. 1080 ref IV opIndexUnary(string op)(auto ref IK k) 1081 { 1082 return editIndex(k, (ref IV e) { mixin("e " ~ op ~ ";"); }); 1083 } 1084 } 1085 } 1086 1087 // *** Mutation (removal) *** 1088 1089 /// Removes all elements with the given key. 1090 bool remove(AK)(auto ref AK key) 1091 if (is(typeof(lookup.remove(key)))) 1092 { 1093 static if (ordered) 1094 { 1095 auto p = key in lookup; 1096 if (!p) 1097 return false; 1098 1099 auto targets = *p; 1100 foreach (target; targets) 1101 { 1102 items = items.remove!(SwapStrategy.stable)(target); 1103 foreach (ref k, ref vs; lookup) 1104 foreach (ref v; vs) 1105 if (v > target) 1106 v--; 1107 } 1108 auto success = lookup.remove(key); 1109 assert(success); 1110 return true; 1111 } 1112 else 1113 return lookup.remove(key); 1114 } 1115 1116 /// Removes all elements. 1117 void clear() 1118 { 1119 lookup.clear(); 1120 static if (ordered) 1121 items.length = 0; 1122 } 1123 } 1124 1125 /// An associative array which retains the order in which elements were added. 1126 alias OrderedMap(K, V) = HashCollection!(K, V, true, false); 1127 1128 unittest 1129 { 1130 alias M = OrderedMap!(string, int); 1131 M m; 1132 m["a"] = 1; 1133 m["b"] = 2; 1134 m["c"] = 3; 1135 assert(m.length == 3); 1136 assert("a" in m); 1137 assert("d" !in m); 1138 1139 { 1140 auto r = m.byKeyValue; 1141 assert(!r.empty); 1142 assert(r.front.key == "a"); 1143 r.popFront(); 1144 assert(!r.empty); 1145 assert(r.front.key == "b"); 1146 r.popFront(); 1147 assert(!r.empty); 1148 assert(r.front.key == "c"); 1149 r.popFront(); 1150 assert(r.empty); 1151 } 1152 1153 assert(m.byKey.equal(["a", "b", "c"])); 1154 assert(m.byValue.equal([1, 2, 3])); 1155 assert(m.byKeyValue.map!(p => p.key).equal(m.byKey)); 1156 assert(m.byKeyValue.map!(p => p.value).equal(m.byValue)); 1157 assert(m.keys == ["a", "b", "c"]); 1158 assert(m.values == [1, 2, 3]); 1159 1160 { 1161 const(M)* c = &m; 1162 assert(c.byKey.equal(["a", "b", "c"])); 1163 assert(c.byValue.equal([1, 2, 3])); 1164 assert(c.keys == ["a", "b", "c"]); 1165 assert(c.values == [1, 2, 3]); 1166 } 1167 1168 m.byValue.front = 5; 1169 assert(m.byValue.equal([5, 2, 3])); 1170 1171 m.remove("a"); 1172 assert(m.length == 2); 1173 m["x"] -= 1; 1174 assert(m["x"] == -1); 1175 ++m["y"]; 1176 assert(m["y"] == 1); 1177 auto cm = cast(const)m.dup; 1178 foreach (k, v; cm) 1179 if (k == "x") 1180 assert(v == -1); 1181 } 1182 1183 unittest 1184 { 1185 OrderedMap!(string, int) m; 1186 m["a"] = 1; 1187 m["b"] = 2; 1188 m.remove("a"); 1189 assert(m["b"] == 2); 1190 } 1191 1192 unittest 1193 { 1194 OrderedMap!(string, int) m; 1195 m["a"] = 1; 1196 auto m2 = m; 1197 m2.remove("a"); 1198 m2["b"] = 2; 1199 assert(m["a"] == 1); 1200 } 1201 1202 unittest 1203 { 1204 OrderedMap!(string, int) m; 1205 m["a"] = 1; 1206 m["b"] = 2; 1207 auto m2 = m; 1208 m.remove("a"); 1209 assert(m2["a"] == 1); 1210 } 1211 1212 unittest 1213 { 1214 class C {} 1215 const OrderedMap!(string, C) m; 1216 cast(void)m.byKeyValue; 1217 } 1218 1219 unittest 1220 { 1221 OrderedMap!(int, int) m; 1222 m.update(10, 1223 { return 20; }, 1224 (ref int k) { k++; return 30; }, 1225 ); 1226 assert(m.length == 1 && m[10] == 20); 1227 m.update(10, 1228 { return 40; }, 1229 (ref int k) { k++; return 50; }, 1230 ); 1231 assert(m.length == 1 && m[10] == 50); 1232 } 1233 1234 // https://issues.dlang.org/show_bug.cgi?id=18606 1235 unittest 1236 { 1237 struct S 1238 { 1239 struct T 1240 { 1241 int foo; 1242 int[] bar; 1243 } 1244 1245 OrderedMap!(int, T) m; 1246 } 1247 } 1248 1249 unittest 1250 { 1251 OrderedMap!(string, int) m; 1252 static assert(is(typeof(m.keys))); 1253 static assert(is(typeof(m.values))); 1254 } 1255 1256 unittest 1257 { 1258 OrderedMap!(string, int) m; 1259 foreach (k, v; m) 1260 k = k ~ k; 1261 } 1262 1263 /// Like assocArray 1264 auto orderedMap(R)(R input) 1265 if (is(typeof(input.front.length) : size_t) && input.front.length == 2) 1266 { 1267 alias K = typeof(input.front[0]); 1268 alias V = typeof(input.front[1]); 1269 return OrderedMap!(K, V)(input); 1270 } 1271 1272 auto orderedMap(R)(R input) /// ditto 1273 if (is(typeof(input.front.key)) && is(typeof(input.front.value)) && !is(typeof(input.front.length))) 1274 { 1275 alias K = typeof(input.front.key); 1276 alias V = typeof(input.front.value); 1277 return OrderedMap!(K, V)(input); 1278 } 1279 1280 unittest 1281 { 1282 auto map = 3.iota.map!(n => tuple(n, n + 1)).orderedMap; 1283 assert(map.length == 3 && map[1] == 2); 1284 } 1285 1286 unittest 1287 { 1288 OrderedMap!(string, int) m; 1289 m = m.byKeyValue.orderedMap; 1290 m = m.byPair.orderedMap; 1291 } 1292 1293 // *************************************************************************** 1294 1295 /// Helper/wrapper for void[0][T] 1296 alias HashSet(T) = HashCollection!(T, void, false, false); 1297 1298 unittest 1299 { 1300 HashSet!int s; 1301 assert(!s); 1302 assert(s.length == 0); 1303 assert(!(1 in s)); 1304 assert(1 !in s); 1305 s.add(1); 1306 assert(1 in s); 1307 assert(s.length == 1); 1308 foreach (k; s) 1309 assert(k == 1); 1310 s.remove(1); 1311 assert(s.length == 0); 1312 1313 s.add(1); 1314 auto t = s.dup; 1315 s.add(2); 1316 assert(t.length==1); 1317 t.remove(1); 1318 assert(t.length==0); 1319 } 1320 1321 unittest 1322 { 1323 struct S { int[int] aa; } 1324 HashSet!S set; 1325 S s; 1326 set.add(s); 1327 assert(s in set); 1328 } 1329 1330 auto toSet(R)(R r) 1331 { 1332 alias E = ElementType!R; 1333 return HashSet!E(r); 1334 } 1335 1336 unittest 1337 { 1338 auto set = [1, 2, 3].toSet(); 1339 assert(2 in set); 1340 assert(4 !in set); 1341 } 1342 1343 unittest 1344 { 1345 HashSet!int m; 1346 const int i; 1347 m.remove(i); 1348 } 1349 1350 unittest 1351 { 1352 HashSet!Object m; 1353 Object o; 1354 m.remove(o); 1355 } 1356 1357 // *************************************************************************** 1358 1359 alias OrderedSet(T) = HashCollection!(T, void, true, false); 1360 1361 unittest 1362 { 1363 OrderedSet!int set; 1364 1365 assert(1 !in set); 1366 set.add(1); 1367 assert(1 in set); 1368 set.remove(1); 1369 assert(1 !in set); 1370 1371 set.add(1); 1372 set.clear(); 1373 assert(1 !in set); 1374 1375 set = set.init; 1376 assert(!set); 1377 set.add(1); 1378 assert(!!set); 1379 1380 assert(set[0] == 1); 1381 set[0] = 2; 1382 assert(set[0] == 2); 1383 assert(1 !in set); 1384 assert(2 in set); 1385 1386 assert(set.length == 1); 1387 set.remove(2); 1388 assert(set.length == 0); 1389 1390 set.add(1); 1391 auto set2 = set; 1392 set.remove(1); 1393 set.add(2); 1394 assert(1 !in set && 2 in set); 1395 assert(1 in set2 && 2 !in set2); 1396 1397 foreach (v; set) 1398 assert(v == 2); 1399 } 1400 1401 auto orderedSet(R)(R r) 1402 { 1403 alias E = ElementType!R; 1404 return OrderedSet!E(r); 1405 } 1406 1407 // *************************************************************************** 1408 1409 /// An object which acts mostly as an associative array, 1410 /// with the added property of being able to hold keys with 1411 /// multiple values. These are only exposed explicitly and 1412 /// through iteration 1413 alias MultiAA(K, V) = HashCollection!(K, V, false, true); 1414 1415 unittest 1416 { 1417 alias MASS = MultiAA!(string, int); 1418 MASS aa; 1419 aa.add("foo", 42); 1420 assert(aa["foo"] == 42); 1421 assert(aa.valuesOf("foo") == [42]); 1422 assert(aa.byPair.front.key == "foo"); 1423 1424 auto aa2 = MASS([tuple("foo", 42)]); 1425 aa2 = ["a":1,"b":2]; 1426 1427 const int i; 1428 aa["a"] = i; 1429 } 1430 1431 unittest 1432 { 1433 MultiAA!(int, int) m; 1434 int[][int] a; 1435 m = a; 1436 }