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