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