1 module orderedmap; 2 3 import std.algorithm; 4 import std.array; 5 import std.container; 6 import std.range; 7 8 public class OrderedMap(T) 9 { 10 private DList!string _orderedKeys; 11 private T[string] _hash; 12 13 public int opApply(int delegate(ref T) dg) 14 { 15 int result = 0; 16 foreach (key; _orderedKeys) 17 { 18 result = dg(_hash[key]); 19 20 if (result) 21 break; 22 } 23 24 return result; 25 } 26 27 public int opApply(int delegate(ref string, ref T) dg) 28 { 29 int result = 0; 30 foreach (key; _orderedKeys) 31 { 32 result = dg(key, _hash[key]); 33 34 if (result) 35 break; 36 } 37 38 return result; 39 } 40 41 public T opIndex(string key) 42 { 43 return _hash[key]; 44 } 45 46 public int opIndexAssign(T val, string key) 47 { 48 if (key in _hash) 49 { 50 _orderedKeys.linearRemove(find(_orderedKeys[], key).take(1)); 51 } 52 53 _hash[key] = val; 54 _orderedKeys.stableInsertBack(key); 55 56 return 0; 57 } 58 59 public bool remove(string key) 60 { 61 auto result = false; 62 result = _hash.remove(key); 63 _orderedKeys.linearRemove(find(_orderedKeys[], key).take(1)); 64 return result; 65 } 66 67 public T* opBinaryRight(string op:"in")(string rhs) 68 { 69 return rhs in _hash; 70 } 71 72 public ulong length() 73 { 74 return _hash.length; 75 } 76 } 77 78 unittest 79 { 80 auto map = new OrderedMap!int(); 81 map["foo"] = 1; 82 map["bar"] = 2; 83 map["bar"] = 3; 84 85 assert(map.length == 2); 86 assert(map["bar"] == 3); 87 assert("foo" in map); 88 map.remove("foo"); 89 assert("foo" !in map); 90 assert(map.length == 1); 91 92 // Test that it actually preserves the insert order 93 map["zyx"] = 99; 94 map["abc"] = 1; 95 map["bar"] = 4; // Should move the existing key to the end 96 97 auto testKeys = ["zyx", "abc", "bar"]; 98 auto testVals = [99, 1, 4]; 99 foreach (key, val; map) 100 { 101 assert(key == testKeys[0]); 102 assert(val == testVals[0]); 103 104 testKeys.popFront(); 105 testVals.popFront(); 106 } 107 }