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 a range of tuples 457 ref typeof(this) opAssign(R)(R input) 458 if (haveValues 459 && is(typeof({ foreach (ref pair; input) add(pair[0], pair[1]); })) 460 && !is(typeof({ foreach (ref k, ref v; input) add(k, v); })) 461 && input.front.length == 2) 462 { 463 clear(); 464 foreach (ref pair; input) 465 add(pair[0], pair[1]); 466 return this; 467 } 468 469 /// Convert from a range of key/value pairs 470 ref typeof(this) opAssign(R)(R input) 471 if (haveValues 472 && is(typeof({ foreach (ref pair; input) add(pair.key, pair.value); })) 473 && !is(typeof({ foreach (ref k, ref v; input) add(k, v); }))) 474 { 475 clear(); 476 foreach (ref pair; input) 477 add(pair.key, pair.value); 478 return this; 479 } 480 481 /// Convert from a range of values 482 ref typeof(this) opAssign(R)(R input) 483 if (!haveValues 484 && !is(R : typeof(this)) 485 && is(typeof({ foreach (ref v; input) add(v); }))) 486 { 487 clear(); 488 foreach (ref v; input) 489 add(v); 490 return this; 491 } 492 493 // *** Conversions (to) *** 494 495 /// Convert to bool (true if non-null) 496 bool opCast(T)() const 497 if (is(T == bool)) 498 { 499 return items !is null; 500 } 501 502 // *** Query (basic) *** 503 504 /// True when there are no items. 505 bool empty() pure const nothrow @nogc @trusted 506 { 507 static if (ordered) 508 return items.length == 0; // optimization 509 else 510 return lookup.byKey.empty; // generic version 511 } 512 513 /// Total number of items, including with duplicate keys. 514 size_t length() pure const nothrow @nogc @trusted 515 { 516 static if (ordered) 517 return items.length; // optimization 518 else 519 static if (!multi) 520 return lookup.length; // optimization 521 else // generic version 522 { 523 size_t result; 524 foreach (ref v; lookup.byValue) 525 result += SM.length(v); 526 return result; 527 } 528 } 529 530 // *** Query (by key) *** 531 532 /// Check if item with this key has been added. 533 /// When applicable, return a pointer to the last value added with this key. 534 Select!(haveReturnType, inout(ReturnType!void)*, bool) opBinaryRight(string op : "in", _K)(auto ref _K key) inout 535 if (is(typeof(key in lookup))) 536 { 537 enum missValue = select!haveReturnType(null, false); 538 539 auto p = key in lookup; 540 if (!p) 541 return missValue; 542 543 static if (haveReturnType) 544 return &lookupToReturnValue((*p)[$-1]); 545 else 546 return true; 547 } 548 549 /// Index operator. 550 /// The key must exist. Indexing with a key which does not exist 551 /// is an error. 552 static if (haveIndexing) 553 ref inout(IV) opIndex()(auto ref IK k) inout 554 { 555 static if (haveValues) 556 return lookupToReturnValue(lookup[k][$-1]); 557 else 558 return items[k].returnValue; 559 } 560 561 /// Retrieve last value associated with key, or `defaultValue` if none. 562 static if (haveIndexing) 563 auto ref inout(IV) get()(auto ref IK k, auto ref inout(IV) defaultValue) inout 564 { 565 static if (haveValues) 566 { 567 auto p = k in lookup; 568 return p ? lookupToReturnValue((*p)[$-1]) : defaultValue; 569 } 570 else 571 return k < items.length ? items[k].returnValue : defaultValue; 572 } 573 574 // *** Query (ranges) *** 575 576 /// Return a range which iterates over key/value pairs. 577 static if (haveValues) 578 auto byKeyValue(this This)() 579 { 580 static if (ordered) 581 return items; 582 else 583 { 584 return lookup 585 .byKeyValue 586 .map!(pair => 587 pair 588 .value 589 .map!(value => KeyValuePair!(K, V)(pair.key, value)) 590 ) 591 .joiner; 592 } 593 } 594 595 /// ditto 596 static if (haveValues) 597 auto byPair(this This)() 598 { 599 return byKeyValue 600 .map!(pair => tuple!("key", "value")(pair.key, pair.value)); 601 } 602 603 /// Return a range which iterates over all keys. 604 /// Duplicate keys will occur several times in the range. 605 auto byKey(this This)() 606 { 607 static if (ordered) 608 { 609 static ref getKey(MItem)(ref MItem item) { return item.key; } 610 return items.map!getKey; 611 } 612 else 613 { 614 return lookup 615 .byKeyValue 616 .map!(pair => 617 pair.key.repeat(SM.length(pair.value)) 618 ) 619 .joiner; 620 } 621 } 622 623 /// Return a range which iterates over all values. 624 static if (haveValues) 625 auto byValue(this This)() 626 { 627 static if (ordered) 628 { 629 static ref getValue(MItem)(ref MItem item) { return item.value; } 630 return items.map!getValue; 631 } 632 else 633 { 634 return lookup 635 .byKeyValue 636 .map!(pair => 637 pair 638 .value 639 ) 640 .joiner; 641 } 642 } 643 644 @property auto keys(this This)() { return byKey.array; } 645 @property auto values(this This)() { return byValue.array; } 646 647 // *** Query (search by key) *** 648 649 static if (ordered) 650 { 651 size_t indexOf()(auto ref const K k) 652 { 653 auto p = k in lookup; 654 return p ? (*p)[0] : -1; 655 } 656 657 size_t[] indicesOf()(auto ref const K k) 658 { 659 auto p = k in lookup; 660 return p ? (*p)[] : null; 661 } 662 } 663 664 /// Return the number of items with the given key. 665 /// When multi==false, always returns 0 or 1. 666 size_t count()(auto ref K k) 667 { 668 static if (ordered) 669 return indicesOf(k).length; 670 else 671 { 672 auto p = k in lookup; 673 return p ? SM.length(*p) : 0; 674 } 675 } 676 677 /// Return a range with all values with the given key. 678 /// If the key is not present, returns an empty range. 679 static if (haveValues) 680 auto byValueOf(this This)(auto ref K k) 681 { 682 static if (ordered) 683 return indicesOf(k).map!(index => items[index].value); 684 else 685 return valuesOf(k); 686 } 687 688 /// Return an array with all values with the given key. 689 /// If the key is not present, returns an empty array. 690 static if (haveValues) 691 V[] valuesOf()(auto ref K k) 692 { 693 static if (ordered) 694 return byValueOf(k).array; 695 else 696 { 697 static if (multi) 698 return lookup.get(k, null); 699 else 700 { 701 auto p = k in lookup; 702 return p ? (*p)[] : null; 703 } 704 } 705 } 706 707 static if (haveValues) 708 deprecated alias getAll = valuesOf; 709 710 // *** Iteration *** 711 712 private int opApplyImpl(this This, Dg)(Dg dg) 713 { 714 enum single = arity!dg == 1; 715 716 int result = 0; 717 718 static if (ordered) 719 { 720 foreach (ref item; items) 721 { 722 static if (single) 723 result = dg(item.returnValue); 724 else 725 result = dg(item.key, item.value); 726 if (result) 727 break; 728 } 729 } 730 else 731 { 732 outer: 733 foreach (ref key, ref values; lookup) 734 static if (haveValues) 735 { 736 foreach (ref value; values) 737 { 738 static if (single) 739 result = dg(value); 740 else 741 result = dg(key, value); 742 if (result) 743 break outer; 744 } 745 } 746 else 747 { 748 foreach (iteration; 0 .. SM.length(values)) 749 { 750 static assert(single); 751 result = dg(key); 752 if (result) 753 break outer; 754 } 755 } 756 } 757 return result; 758 } 759 760 /// Iterate over keys (sets) / values (maps). 761 int opApply(int delegate(ref SingleIterationType x) dg) 762 { 763 return opApplyImpl(dg); 764 } 765 766 /// ditto 767 int opApply(int delegate(const ref SingleIterationType x) dg) const 768 { 769 return opApplyImpl(dg); 770 } 771 772 static if (haveValues) 773 { 774 /// Iterate over keys and values. 775 int opApply(int delegate(K k, ref V v) dg) 776 { 777 return opApplyImpl(dg); 778 } 779 780 /// ditto 781 int opApply(int delegate(K k, const ref V v) dg) const 782 { 783 return opApplyImpl(dg); 784 } 785 } 786 787 // *** Mutation (addition) *** 788 789 private enum AddMode 790 { 791 add, /// Always add value 792 replace, /// Replace all previous values 793 require, /// Only add value if it did not exist before 794 } 795 796 private ref ReturnType!void addImpl(AddMode mode, AK, GV)(ref AK key, scope GV getValue) 797 if (is(AK : K)) 798 { 799 static if (ordered) 800 { 801 size_t addedIndex; 802 803 static if (multi && mode == AddMode.add) 804 { 805 addedIndex = items.length; 806 lookup[key] ~= addedIndex; 807 items ~= Item(key, getValue()); 808 } 809 else 810 { 811 lookup.updateVoid(key, 812 delegate LookupValue() 813 { 814 addedIndex = items.length; 815 items ~= Item(key, getValue()); 816 return [addedIndex]; 817 }, 818 delegate void(ref LookupValue existingIndex) 819 { 820 addedIndex = existingIndex[0]; 821 static if (mode != AddMode.require) 822 { 823 static if (multi) 824 { 825 static assert(mode == AddMode.replace); 826 existingIndex = existingIndex[0 .. 1]; 827 } 828 items[addedIndex].value = getValue(); 829 } 830 }); 831 } 832 833 return items[addedIndex].returnValue; 834 } 835 else // ordered 836 { 837 static if (haveValues) 838 { 839 static if (mode == AddMode.require) 840 return (lookup.require(key, [getValue()]))[0]; 841 else 842 static if (multi && mode == AddMode.add) 843 return (lookup[key] ~= getValue())[$-1]; 844 else 845 return (lookup[key] = [getValue()])[0]; 846 } 847 else 848 { 849 static if (multi) 850 { 851 static if (mode == AddMode.require) 852 lookup.require(key, 1); 853 else 854 static if (mode == AddMode.add) 855 lookup[key]++; 856 else 857 lookup[key] = 1; 858 } 859 else 860 lookup[key] = LookupValue.init; 861 // This branch returns void, as there is no reasonable 862 // ref to an AA key that we can return here. 863 } 864 } 865 } 866 867 /*private*/ template addSetFunc(AddMode mode) 868 { 869 static if (haveValues) 870 { 871 ref ReturnType!void addSetFunc(AK, AV)(auto ref AK key, auto ref AV value) 872 if (is(AK : K) && is(AV : V)) 873 { 874 return addImpl!mode(key, () => value); 875 } 876 } 877 else 878 { 879 ref ReturnType!void addSetFunc(AK)(auto ref AK key) 880 if (is(AK : K)) 881 { 882 ValueVarType value; // void[0] 883 return addImpl!mode(key, () => value); 884 } 885 } 886 } 887 888 /// Add an item. 889 alias add = addSetFunc!(AddMode.add); 890 891 /// Ensure a key exists (with the given value). 892 /// When `multi==true`, replaces all previous entries with this key. 893 /// Otherwise, behaves identically to `add`. 894 alias set = addSetFunc!(AddMode.replace); 895 896 /// Add `value` only if `key` is not present. 897 static if (haveValues) 898 ref V require()(auto ref K key, lazy V value = V.init) 899 { 900 return addImpl!(AddMode.require)(key, () => value); 901 } 902 903 deprecated alias getOrAdd = require; 904 905 private alias UpdateFuncRT(U) = typeof({ U u = void; V v = void; return u(v); }()); 906 907 /// If `key` is present, call `update` for every value; 908 /// otherwise, add new value with `create`. 909 static if (haveValues) 910 void update(C, U)(auto ref K key, scope C create, scope U update) 911 if (is(typeof(create()) : V) && (is(UpdateFuncRT!U : V) || is(UpdateFuncRT!U == void))) 912 { 913 static if (ordered) 914 { 915 lookup.updateVoid(key, 916 delegate LookupValue() 917 { 918 auto addedIndex = items.length; 919 items ~= Item(key, create()); 920 return [addedIndex]; 921 }, 922 delegate void(ref LookupValue existingIndex) 923 { 924 foreach (i; existingIndex) 925 static if (is(UpdateFuncRT!U == void)) 926 update(items[i].value); 927 else 928 items[i].value = update(items[i].value); 929 }); 930 } 931 else // ordered 932 { 933 lookup.updateVoid(key, 934 delegate LookupValue () 935 { 936 return [create()]; 937 }, 938 delegate void (ref LookupValue values) 939 { 940 foreach (ref value; values) 941 static if (is(UpdateFuncRT!U == void)) 942 update(value); 943 else 944 value = update(value); 945 }); 946 } 947 } 948 949 // *** Mutation (editing) *** 950 951 static if (haveIndexing) 952 { 953 static if (haveValues) 954 { 955 /// Same as `set(k, v)`. 956 ref IV opIndexAssign()(auto ref IV v, auto ref IK k) 957 { 958 return set(k, v); 959 } 960 961 /// Perform cumulative operation with value 962 /// (initialized with `.init` if the key does not exist). 963 ref IV opIndexOpAssign(string op)(auto ref IV v, auto ref IK k) 964 { 965 auto pv = &require(k); 966 return mixin("(*pv) " ~ op ~ "= v"); 967 } 968 969 /// Perform unary operation with value 970 /// (initialized with `.init` if the key does not exist). 971 ref IV opIndexUnary(string op)(auto ref IK k) 972 { 973 auto pv = &require(k); 974 mixin("(*pv) " ~ op ~ ";"); 975 return *pv; 976 } 977 } 978 else 979 { 980 private ref K editIndex(size_t index, scope void delegate(ref K) edit) 981 { 982 auto item = &items[index]; 983 K oldKey = item.key; 984 auto pOldIndices = oldKey in lookup; 985 assert(pOldIndices); 986 987 edit(item.key); 988 989 // Add new value 990 991 lookup.updateVoid(item.key, 992 delegate LookupValue() 993 { 994 // New value did not exist. 995 if ((*pOldIndices).length == 1) 996 { 997 // Optimization - migrate the Indexes value 998 assert((*pOldIndices)[0] == index); 999 return *pOldIndices; 1000 } 1001 else 1002 return [index]; 1003 }, 1004 delegate void(ref LookupValue existingIndex) 1005 { 1006 // Value(s) with the new key already existed 1007 static if (multi) 1008 existingIndex ~= index; 1009 else 1010 assert(false, "Collision after in-place edit of a non-multi ordered set element"); 1011 }); 1012 1013 // Remove old value 1014 1015 if ((*pOldIndices).length == 1) 1016 lookup.remove(oldKey); 1017 else 1018 static if (multi) 1019 *pOldIndices = (*pOldIndices).remove!(i => i == index); 1020 else 1021 assert(false); // Should be unreachable (`if` above will always be true) 1022 1023 return item.key; 1024 } 1025 1026 /// Allows writing to ordered sets by index. 1027 /// The total number of elements never changes as a result 1028 /// of such an operation - a consequence of which is that 1029 /// if multi==false, changing the value to one that's 1030 /// already in the set is an error. 1031 ref IV opIndexAssign()(auto ref IV v, auto ref IK k) 1032 { 1033 static if (haveValues) 1034 return set(k, v); 1035 else 1036 return editIndex(k, (ref IV e) { e = v; }); 1037 } 1038 1039 /// Perform cumulative operation with value at index. 1040 ref IV opIndexOpAssign(string op)(auto ref VV v, auto ref IK k) 1041 { 1042 return editIndex(k, (ref IV e) { mixin("e " ~ op ~ "= v;"); }); 1043 } 1044 1045 /// Perform unary operation with value at index. 1046 ref IV opIndexUnary(string op)(auto ref IK k) 1047 { 1048 return editIndex(k, (ref IV e) { mixin("e " ~ op ~ ";"); }); 1049 } 1050 } 1051 } 1052 1053 // *** Mutation (removal) *** 1054 1055 /// Removes all elements with the given key. 1056 bool remove()(auto ref K key) 1057 { 1058 static if (ordered) 1059 { 1060 auto p = key in lookup; 1061 if (!p) 1062 return false; 1063 1064 auto targets = *p; 1065 foreach (target; targets) 1066 { 1067 items = items.remove!(SwapStrategy.stable)(target); 1068 foreach (ref k, ref vs; lookup) 1069 foreach (ref v; vs) 1070 if (v > target) 1071 v--; 1072 } 1073 auto success = lookup.remove(key); 1074 assert(success); 1075 return true; 1076 } 1077 else 1078 return lookup.remove(key); 1079 } 1080 1081 /// Removes all elements. 1082 void clear() 1083 { 1084 lookup.clear(); 1085 static if (ordered) 1086 items.length = 0; 1087 } 1088 } 1089 1090 /// An associative array which retains the order in which elements were added. 1091 alias OrderedMap(K, V) = HashCollection!(K, V, true, false); 1092 1093 unittest 1094 { 1095 alias M = OrderedMap!(string, int); 1096 M m; 1097 m["a"] = 1; 1098 m["b"] = 2; 1099 m["c"] = 3; 1100 assert(m.length == 3); 1101 assert("a" in m); 1102 assert("d" !in m); 1103 1104 { 1105 auto r = m.byKeyValue; 1106 assert(!r.empty); 1107 assert(r.front.key == "a"); 1108 r.popFront(); 1109 assert(!r.empty); 1110 assert(r.front.key == "b"); 1111 r.popFront(); 1112 assert(!r.empty); 1113 assert(r.front.key == "c"); 1114 r.popFront(); 1115 assert(r.empty); 1116 } 1117 1118 assert(m.byKey.equal(["a", "b", "c"])); 1119 assert(m.byValue.equal([1, 2, 3])); 1120 assert(m.byKeyValue.map!(p => p.key).equal(m.byKey)); 1121 assert(m.byKeyValue.map!(p => p.value).equal(m.byValue)); 1122 assert(m.keys == ["a", "b", "c"]); 1123 assert(m.values == [1, 2, 3]); 1124 1125 { 1126 const(M)* c = &m; 1127 assert(c.byKey.equal(["a", "b", "c"])); 1128 assert(c.byValue.equal([1, 2, 3])); 1129 assert(c.keys == ["a", "b", "c"]); 1130 assert(c.values == [1, 2, 3]); 1131 } 1132 1133 m.byValue.front = 5; 1134 assert(m.byValue.equal([5, 2, 3])); 1135 1136 m.remove("a"); 1137 assert(m.length == 2); 1138 m["x"] -= 1; 1139 assert(m["x"] == -1); 1140 ++m["y"]; 1141 assert(m["y"] == 1); 1142 auto cm = cast(const)m.dup; 1143 foreach (k, v; cm) 1144 if (k == "x") 1145 assert(v == -1); 1146 } 1147 1148 unittest 1149 { 1150 OrderedMap!(string, int) m; 1151 m["a"] = 1; 1152 m["b"] = 2; 1153 m.remove("a"); 1154 assert(m["b"] == 2); 1155 } 1156 1157 unittest 1158 { 1159 OrderedMap!(string, int) m; 1160 m["a"] = 1; 1161 auto m2 = m; 1162 m2.remove("a"); 1163 m2["b"] = 2; 1164 assert(m["a"] == 1); 1165 } 1166 1167 unittest 1168 { 1169 OrderedMap!(string, int) m; 1170 m["a"] = 1; 1171 m["b"] = 2; 1172 auto m2 = m; 1173 m.remove("a"); 1174 assert(m2["a"] == 1); 1175 } 1176 1177 unittest 1178 { 1179 class C {} 1180 const OrderedMap!(string, C) m; 1181 cast(void)m.byKeyValue; 1182 } 1183 1184 unittest 1185 { 1186 OrderedMap!(int, int) m; 1187 m.update(10, 1188 { return 20; }, 1189 (ref int k) { k++; return 30; }, 1190 ); 1191 assert(m.length == 1 && m[10] == 20); 1192 m.update(10, 1193 { return 40; }, 1194 (ref int k) { k++; return 50; }, 1195 ); 1196 assert(m.length == 1 && m[10] == 50); 1197 } 1198 1199 // https://issues.dlang.org/show_bug.cgi?id=18606 1200 unittest 1201 { 1202 struct S 1203 { 1204 struct T 1205 { 1206 int foo; 1207 int[] bar; 1208 } 1209 1210 OrderedMap!(int, T) m; 1211 } 1212 } 1213 1214 unittest 1215 { 1216 OrderedMap!(string, int) m; 1217 static assert(is(typeof(m.keys))); 1218 static assert(is(typeof(m.values))); 1219 } 1220 1221 unittest 1222 { 1223 OrderedMap!(string, int) m; 1224 foreach (k, v; m) 1225 k = k ~ k; 1226 } 1227 1228 /// Like assocArray 1229 auto orderedMap(R)(R input) 1230 if (is(typeof(input.front.length) : size_t) && input.front.length == 2) 1231 { 1232 alias K = typeof(input.front[0]); 1233 alias V = typeof(input.front[1]); 1234 return OrderedMap!(K, V)(input); 1235 } 1236 1237 auto orderedMap(R)(R input) /// ditto 1238 if (is(typeof(input.front.key)) && is(typeof(input.front.value)) && !is(typeof(input.front.length))) 1239 { 1240 alias K = typeof(input.front.key); 1241 alias V = typeof(input.front.value); 1242 return OrderedMap!(K, V)(input); 1243 } 1244 1245 unittest 1246 { 1247 auto map = 3.iota.map!(n => tuple(n, n + 1)).orderedMap; 1248 assert(map.length == 3 && map[1] == 2); 1249 } 1250 1251 unittest 1252 { 1253 OrderedMap!(string, int) m; 1254 m = m.byKeyValue.orderedMap; 1255 m = m.byPair.orderedMap; 1256 } 1257 1258 // *************************************************************************** 1259 1260 /// Helper/wrapper for void[0][T] 1261 alias HashSet(T) = HashCollection!(T, void, false, false); 1262 1263 unittest 1264 { 1265 HashSet!int s; 1266 assert(s.length == 0); 1267 assert(!(1 in s)); 1268 assert(1 !in s); 1269 s.add(1); 1270 assert(1 in s); 1271 assert(s.length == 1); 1272 foreach (k; s) 1273 assert(k == 1); 1274 s.remove(1); 1275 assert(s.length == 0); 1276 1277 s.add(1); 1278 auto t = s.dup; 1279 s.add(2); 1280 assert(t.length==1); 1281 t.remove(1); 1282 assert(t.length==0); 1283 } 1284 1285 unittest 1286 { 1287 struct S { int[int] aa; } 1288 HashSet!S set; 1289 S s; 1290 set.add(s); 1291 assert(s in set); 1292 } 1293 1294 auto toSet(R)(R r) 1295 { 1296 alias E = ElementType!R; 1297 return HashSet!E(r); 1298 } 1299 1300 unittest 1301 { 1302 auto set = [1, 2, 3].toSet(); 1303 assert(2 in set); 1304 assert(4 !in set); 1305 } 1306 1307 // *************************************************************************** 1308 1309 alias OrderedSet(T) = HashCollection!(T, void, true, false); 1310 1311 unittest 1312 { 1313 OrderedSet!int set; 1314 1315 assert(1 !in set); 1316 set.add(1); 1317 assert(1 in set); 1318 set.remove(1); 1319 assert(1 !in set); 1320 1321 set.add(1); 1322 set.clear(); 1323 assert(1 !in set); 1324 1325 set = set.init; 1326 assert(!set); 1327 set.add(1); 1328 assert(!!set); 1329 1330 assert(set[0] == 1); 1331 set[0] = 2; 1332 assert(set[0] == 2); 1333 assert(1 !in set); 1334 assert(2 in set); 1335 1336 assert(set.length == 1); 1337 set.remove(2); 1338 assert(set.length == 0); 1339 1340 set.add(1); 1341 auto set2 = set; 1342 set.remove(1); 1343 set.add(2); 1344 assert(1 !in set && 2 in set); 1345 assert(1 in set2 && 2 !in set2); 1346 1347 foreach (v; set) 1348 assert(v == 2); 1349 } 1350 1351 auto orderedSet(R)(R r) 1352 { 1353 alias E = ElementType!R; 1354 return OrderedSet!E(r); 1355 } 1356 1357 // *************************************************************************** 1358 1359 /// An object which acts mostly as an associative array, 1360 /// with the added property of being able to hold keys with 1361 /// multiple values. These are only exposed explicitly and 1362 /// through iteration 1363 alias MultiAA(K, V) = HashCollection!(K, V, false, true); 1364 1365 unittest 1366 { 1367 alias MASS = MultiAA!(string, int); 1368 MASS aa; 1369 aa.add("foo", 42); 1370 assert(aa["foo"] == 42); 1371 assert(aa.valuesOf("foo") == [42]); 1372 assert(aa.byPair.front.key == "foo"); 1373 1374 auto aa2 = MASS([tuple("foo", 42)]); 1375 aa2 = ["a":1,"b":2]; 1376 }