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