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 }