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.typecons; 19 20 // *************************************************************************** 21 22 /// Get a value from an AA, and throw an exception (not an error) if not found 23 ref auto aaGet(AA, K)(auto ref AA aa, K key) 24 if (is(typeof(key in aa))) 25 { 26 import std.conv; 27 28 auto p = key in aa; 29 if (p) 30 return *p; 31 else 32 static if (is(typeof(text(key)))) 33 throw new Exception("Absent value: " ~ text(key)); 34 else 35 throw new Exception("Absent value"); 36 } 37 38 /// If key is not in aa, add it with defaultValue. 39 /// Returns a reference to the value corresponding to key. 40 ref V getOrAdd(K, V)(ref V[K] aa, K key, V defaultValue = V.init) 41 { 42 auto p = key in aa; 43 if (!p) 44 { 45 aa[key] = defaultValue; 46 p = key in aa; 47 } 48 return *p; 49 } 50 51 unittest 52 { 53 int[int] aa; 54 aa.getOrAdd(1, 2) = 3; 55 assert(aa[1] == 3); 56 assert(aa.getOrAdd(1, 4) == 3); 57 } 58 59 struct KeyValuePair(K, V) { K key; V value; } 60 61 /// Get key/value pairs from AA 62 KeyValuePair!(K, V)[] pairs(K, V)(V[K] aa) 63 { 64 KeyValuePair!(K, V)[] result; 65 foreach (key, value; aa) 66 result ~= KeyValuePair!(K, V)(key, value); 67 return result; 68 } 69 70 /// Get key/value pairs from AA, sorted by keys 71 KeyValuePair!(K, V)[] sortedPairs(K, V)(V[K] aa) 72 { 73 KeyValuePair!(K, V)[] result; 74 foreach (key; aa.keys.sort) 75 result ~= KeyValuePair!(K, V)(key, aa[key]); 76 return result; 77 } 78 79 /// Get values from AA, sorted by keys 80 V[] sortedValues(K, V)(in V[K] aa) 81 { 82 V[] result; 83 foreach (key; aa.keys.sort()) 84 result ~= aa[key]; 85 return result; 86 } 87 88 /// Merge source into target. Return target. 89 V[K] merge(K, V)(auto ref V[K] target, in V[K] source) 90 { 91 foreach (k, v; source) 92 target[k] = v; 93 return target; 94 } 95 96 unittest 97 { 98 int[int] target; 99 int[int] source = [2:4]; 100 merge(target, source); 101 assert(source == target); 102 103 target = [1:1, 2:2, 3:3]; 104 merge(target, source); 105 assert(target == [1:1, 2:4, 3:3]); 106 107 assert(merge([1:1], [2:2]) == [1:1, 2:2]); 108 } 109 110 /// Slurp a range of two elements (or two-element struct/class) into an AA. 111 auto toAA(R)(R r) 112 if (is(typeof(r.front[1]))) 113 { 114 alias K = typeof(r.front[0]); 115 alias V = typeof(r.front[1]); 116 V[K] result; 117 foreach (pair; r) 118 { 119 assert(pair.length == 2); 120 result[pair[0]] = pair[1]; 121 } 122 return result; 123 } 124 125 /// ditto 126 auto toAA(R)(R r) 127 if (is(typeof(r.front.tupleof)) && r.front.tupleof.length == 2 && !is(typeof(r.front[1]))) 128 { 129 return r.map!(el => tuple(el.tupleof)).toAA(); 130 } 131 132 unittest 133 { 134 assert([[2, 4]].toAA() == [2:4]); 135 assert([2:4].pairs.toAA() == [2:4]); 136 } 137 138 // *************************************************************************** 139 140 /// An associative array which retains the order in which elements were added. 141 struct OrderedMap(K, V) 142 { 143 K[] keys; 144 V[] values; 145 size_t[K] index; 146 147 /// Convert from regular AA 148 this(V[K] aa) 149 { 150 opAssign(aa); 151 } 152 153 static if (is(typeof(keys.dup && values.dup && index.dup))) 154 { 155 this(this) 156 { 157 keys = keys.dup; 158 values = values.dup; 159 index = index.dup; 160 } 161 } 162 else 163 @disable this(this); 164 165 void opAssign(V[K] aa) 166 { 167 clear(); 168 foreach (ref k, ref v; aa) 169 { 170 index[k] = values.length; 171 keys ~= k; 172 values ~= v; 173 } 174 } 175 176 void clear() 177 { 178 keys = null; 179 values = null; 180 index = null; 181 } 182 183 bool opCast(T)() const 184 if (is(T == bool)) 185 { 186 return !!index; 187 } 188 189 ref inout(V) opIndex()(auto ref K k) inout 190 { 191 return values[index[k]]; 192 } 193 194 ref V opIndexAssign()(auto ref V v, auto ref K k) 195 { 196 auto pi = k in index; 197 if (pi) 198 { 199 auto pv = &values[*pi]; 200 *pv = v; 201 return *pv; 202 } 203 204 index[k] = values.length; 205 keys ~= k; 206 values ~= v; 207 return values[$-1]; 208 } 209 210 ref V getOrAdd()(auto ref K k) 211 { 212 auto pi = k in index; 213 V* pv; 214 if (pi) 215 pv = &values[*pi]; 216 else 217 { 218 index[k] = values.length; 219 keys ~= k; 220 values ~= V.init; 221 pv = &values[$-1]; 222 } 223 return *pv; 224 } 225 226 ref V opIndexUnary(string op)(auto ref K k) 227 { 228 auto pv = &getOrAdd(k); 229 mixin("(*pv) " ~ op ~ ";"); 230 return *pv; 231 } 232 233 ref V opIndexOpAssign(string op)(auto ref V v, auto ref K k) 234 { 235 auto pv = &getOrAdd(k); 236 mixin("(*pv) " ~ op ~ "= v;"); 237 return *pv; 238 } 239 240 inout(V) get()(auto ref K k, inout(V) defaultValue) inout 241 { 242 auto p = k in index; 243 return p ? values[*p] : defaultValue; 244 } 245 246 inout(V)* opIn_r()(auto ref in K k) inout 247 { 248 auto p = k in index; 249 return p ? &values[*p] : null; 250 } 251 252 void remove()(auto ref K k) 253 { 254 auto i = index[k]; 255 index.remove(k); 256 keys = keys.remove(i); 257 values = values.remove(i); 258 foreach (key, ref idx; index) 259 if (idx > i) 260 idx--; 261 } 262 263 @property size_t length() const { return values.length; } 264 265 private int opApplyImpl(this This, Dg)(Dg dg) 266 { 267 int result = 0; 268 269 foreach (i, ref v; values) 270 { 271 result = dg(keys[i], v); 272 if (result) 273 break; 274 } 275 return result; 276 } 277 278 int opApply(int delegate(ref K k, ref V v) dg) 279 { 280 return opApplyImpl(dg); 281 } 282 283 int opApply(int delegate(const ref K k, const ref V v) dg) const 284 { 285 return opApplyImpl(dg); 286 } 287 288 @property typeof(this) dup() 289 { 290 typeof(this) result; 291 result.keys = keys.dup; 292 result.values = values.dup; 293 result.index = index.dup; 294 return result; 295 } 296 297 alias byKey = keys; 298 alias byValue = values; 299 300 auto byKeyValue(this T)() 301 { 302 auto instance = this; 303 struct Range 304 { 305 size_t index; 306 bool empty() const { return index == instance.values.length; } 307 static struct KeyValue { typeof(instance.keys[0]) key; typeof(instance.values[0]) value; } 308 KeyValue front() { return KeyValue(instance.keys[index], instance.values[index]); } 309 void popFront() { index++; } 310 } 311 return Range(); 312 } 313 } 314 315 unittest 316 { 317 OrderedMap!(string, int) m; 318 m["a"] = 1; 319 m["b"] = 2; 320 m["c"] = 3; 321 assert(m.length == 3); 322 assert("a" in m); 323 assert("d" !in m); 324 325 { 326 auto r = m.byKeyValue; 327 assert(!r.empty); 328 assert(r.front.key == "a"); 329 r.popFront(); 330 assert(!r.empty); 331 assert(r.front.key == "b"); 332 r.popFront(); 333 assert(!r.empty); 334 assert(r.front.key == "c"); 335 r.popFront(); 336 assert(r.empty); 337 } 338 339 m.remove("a"); 340 assert(m.length == 2); 341 m["x"] -= 1; 342 assert(m["x"] == -1); 343 ++m["y"]; 344 assert(m["y"] == 1); 345 auto cm = cast(const)m.dup; 346 foreach (k, v; cm) 347 if (k == "x") 348 assert(v == -1); 349 } 350 351 unittest 352 { 353 OrderedMap!(string, int) m; 354 m["a"] = 1; 355 m["b"] = 2; 356 m.remove("a"); 357 assert(m["b"] == 2); 358 } 359 360 unittest 361 { 362 OrderedMap!(string, int) m; 363 m["a"] = 1; 364 auto m2 = m; 365 m2.remove("a"); 366 m2["b"] = 2; 367 assert(m["a"] == 1); 368 } 369 370 unittest 371 { 372 OrderedMap!(string, int) m; 373 m["a"] = 1; 374 m["b"] = 2; 375 auto m2 = m; 376 m.remove("a"); 377 assert(m2["a"] == 1); 378 } 379 380 unittest 381 { 382 class C {} 383 const OrderedMap!(string, C) m; 384 m.byKeyValue; 385 } 386 387 // https://issues.dlang.org/show_bug.cgi?id=18606 388 unittest 389 { 390 struct S 391 { 392 struct T 393 { 394 int foo; 395 int[] bar; 396 } 397 398 OrderedMap!(int, T) m; 399 } 400 } 401 402 // *************************************************************************** 403 404 /// Helper/wrapper for void[0][T] 405 struct HashSet(T) 406 { 407 void[0][T] data; 408 409 alias data this; 410 411 this(R)(R r) 412 { 413 foreach (k; r) 414 add(k); 415 } 416 417 void add(T k) 418 { 419 void[0] v; 420 data[k] = v; 421 } 422 423 void remove(T k) 424 { 425 data.remove(k); 426 } 427 428 @property HashSet!T dup() const 429 { 430 // Can't use .dup with void[0] value 431 HashSet!T result; 432 foreach (k, v; data) 433 result.add(k); 434 return result; 435 } 436 437 int opApply(scope int delegate(ref T) dg) 438 { 439 int result; 440 foreach (k, v; data) 441 if ((result = dg(k)) != 0) 442 break; 443 return result; 444 } 445 } 446 447 unittest 448 { 449 HashSet!int s; 450 assert(s.length == 0); 451 assert(!(1 in s)); 452 assert(1 !in s); 453 s.add(1); 454 assert(1 in s); 455 assert(s.length == 1); 456 foreach (k; s) 457 assert(k == 1); 458 s.remove(1); 459 assert(s.length == 0); 460 461 s.add(1); 462 auto t = s.dup; 463 s.add(2); 464 assert(t.length==1); 465 t.remove(1); 466 assert(t.length==0); 467 } 468 469 auto toSet(R)(R r) 470 { 471 alias E = ElementType!R; 472 return HashSet!E(r); 473 } 474 475 unittest 476 { 477 auto set = [1, 2, 3].toSet(); 478 assert(2 in set); 479 assert(4 !in set); 480 } 481 482 // *************************************************************************** 483 484 struct OrderedSet(T) 485 { 486 T[] items; 487 size_t[T] index; 488 489 this(R)(R r) 490 if (isInputRange!R) 491 { 492 foreach (k; r) 493 add(k); 494 } 495 496 static if (is(typeof(items.dup && index.dup))) 497 { 498 this(this) 499 { 500 items = items.dup; 501 index = index.dup; 502 } 503 } 504 else 505 @disable this(this); 506 507 void clear() 508 { 509 items = null; 510 index = null; 511 } 512 513 bool opCast(T)() const 514 if (is(T == bool)) 515 { 516 return !!index; 517 } 518 519 ref inout(T) opIndex()(size_t i) inout 520 { 521 return items[i]; 522 } 523 524 ref T opIndexAssign()(auto ref T v, size_t i) 525 { 526 assert(i < items.length); 527 index.remove(items[i]); 528 items[i] = v; 529 index[v] = i; 530 return items[i]; 531 } 532 533 bool opIn_r()(auto ref in T v) inout 534 { 535 return !!(v in index); 536 } 537 538 ref T add()(auto ref T v) 539 { 540 auto pi = v in index; 541 if (pi) 542 { 543 auto pv = &items[*pi]; 544 *pv = v; 545 return *pv; 546 } 547 548 index[v] = items.length; 549 items ~= v; 550 return items[$-1]; 551 } 552 553 void remove()(auto ref T v) 554 { 555 auto i = index[v]; 556 index.remove(v); 557 items = items.remove(i); 558 foreach (key, ref idx; index) 559 if (idx > i) 560 idx--; 561 } 562 563 @property size_t length() const { return items.length; } 564 565 @property typeof(this) dup() 566 { 567 typeof(this) result; 568 result.items = items.dup; 569 result.index = index.dup; 570 return result; 571 } 572 } 573 574 unittest 575 { 576 OrderedSet!int set; 577 578 assert(1 !in set); 579 set.add(1); 580 assert(1 in set); 581 set.remove(1); 582 assert(1 !in set); 583 584 set.add(1); 585 set.clear(); 586 assert(1 !in set); 587 588 set = set.init; 589 assert(!set); 590 set.add(1); 591 assert(!!set); 592 593 assert(set[0] == 1); 594 set[0] = 2; 595 assert(set[0] == 2); 596 assert(1 !in set); 597 assert(2 in set); 598 599 assert(set.length == 1); 600 set.remove(2); 601 assert(set.length == 0); 602 603 set.add(1); 604 auto set2 = set; 605 set.remove(1); 606 set.add(2); 607 assert(1 !in set && 2 in set); 608 assert(1 in set2 && 2 !in set2); 609 } 610 611 // *************************************************************************** 612 613 /// An object which acts mostly as an associative array, 614 /// with the added property of being able to hold keys with 615 /// multiple values. These are only exposed explicitly and 616 /// through iteration 617 struct MultiAA(K, V) 618 { 619 V[][K] items; 620 621 /// If multiple items with this name are present, 622 /// only the first one is returned. 623 ref inout(V) opIndex(K key) inout 624 { 625 return items[key][0]; 626 } 627 628 V opIndexAssign(V value, K key) 629 { 630 items[key] = [value]; 631 return value; 632 } 633 634 inout(V)* opIn_r(K key) inout @nogc 635 { 636 auto pvalues = key in items; 637 if (pvalues && (*pvalues).length) 638 return &(*pvalues)[0]; 639 return null; 640 } 641 642 void remove(K key) 643 { 644 items.remove(key); 645 } 646 647 // D forces these to be "ref" 648 int opApply(int delegate(ref K key, ref V value) dg) 649 { 650 int ret; 651 outer: 652 foreach (key, values; items) 653 foreach (ref value; values) 654 { 655 ret = dg(key, value); 656 if (ret) 657 break outer; 658 } 659 return ret; 660 } 661 662 // Copy-paste because of https://issues.dlang.org/show_bug.cgi?id=7543 663 int opApply(int delegate(ref const(K) key, ref const(V) value) dg) const 664 { 665 int ret; 666 outer: 667 foreach (key, values; items) 668 foreach (ref value; values) 669 { 670 ret = dg(key, value); 671 if (ret) 672 break outer; 673 } 674 return ret; 675 } 676 677 void add(K key, V value) 678 { 679 if (key !in items) 680 items[key] = [value]; 681 else 682 items[key] ~= value; 683 } 684 685 V get(K key, lazy V def) const 686 { 687 auto pvalue = key in this; 688 return pvalue ? *pvalue : def; 689 } 690 691 inout(V)[] getAll(K key) inout 692 { 693 inout(V)[] result; 694 foreach (ref value; items.get(key, null)) 695 result ~= value; 696 return result; 697 } 698 699 this(typeof(null) Null) 700 { 701 } 702 703 this(V[K] aa) 704 { 705 foreach (ref key, ref value; aa) 706 add(key, value); 707 } 708 709 this(V[][K] aa) 710 { 711 foreach (ref key, values; aa) 712 foreach (ref value; values) 713 add(key, value); 714 } 715 716 @property auto keys() inout { return items.keys; } 717 718 // https://issues.dlang.org/show_bug.cgi?id=14626 719 720 @property V[] values() 721 { 722 return items.byValue.join; 723 } 724 725 @property const(V)[] values() const 726 { 727 return items.byValue.join; 728 } 729 730 @property typeof(V[K].init.pairs) pairs() 731 { 732 alias Pair = typeof(V[K].init.pairs[0]); 733 Pair[] result; 734 result.reserve(length); 735 foreach (ref k, ref v; this) 736 result ~= Pair(k, v); 737 return result; 738 } 739 740 @property size_t length() const { return items.byValue.map!(item => item.length).sum(); } 741 742 auto byKey() { return items.byKey(); } 743 auto byValue() { return items.byValue().joiner(); } 744 745 bool opCast(T)() inout 746 if (is(T == bool)) 747 { 748 return !!items; 749 } 750 751 /// Warning: discards repeating items 752 V[K] opCast(T)() const 753 if (is(T == V[K])) 754 { 755 V[K] result; 756 foreach (key, value; this) 757 result[key] = value; 758 return result; 759 } 760 761 V[][K] opCast(T)() inout 762 if (is(T == V[][K])) 763 { 764 V[][K] result; 765 foreach (k, v; this) 766 result[k] ~= v; 767 return result; 768 } 769 } 770 771 unittest 772 { 773 MultiAA!(string, string) aa; 774 }