1 module kontainer.orderedAssocArray.orderedAssocArray;
2 
3  /**
4  * The associative array that an order was guaranteed.
5  *
6  * Why this library was necessary:
7  *   D's Associative Array has a (critical) trouble:
8  *     int[string] foo = [
9  *       "abc"    : 123,
10  *       "k"      : 3874,
11  *       "abcdef" : 0];
12  *    foreach (key, value; foo) {
13  *      writeln("[key : value] - ", [key : value]);
14  *    }
15  *  Then, We(at least I) expect naturally, the above codes will output as:
16  *    [key : value] - ["abc":123]
17  *    [key : value] - ["k":3874]
18  *    [key : value] - ["abcdef":0]
19  *  However, the above codes prints:
20  *    [key : value] - ["abc":123]
21  *    [key : value] - ["abcdef":0]
22  *    [key : value] - ["k":3874]
23  *
24  *  The result means that D's Associative Array doesn't guarantee to keep the order(insert order).
25  *  This container guarantee to keep the order.
26  *
27  *
28  * Simple Usage:
29  *  import kontainer.orderedAssocArray;
30  * 
31  *  void main() {
32  *    OrderedAssocArray!(string, int) oaa = new OrderedAssocArray!(string, int);
33  *    oaa["abc"] = 123;
34  *    oaa["k"]   = 3874;
35  *    oaa["abcdef"] = 0;
36  *
37  *    foreach (pair; oaa) {
38  *      // Access key and value with each of it as property.
39  *      writeln(pair.key, " - ", pair.value);
40  *    }
41  *
42  *    // You can modify like D's associative array
43  *    oaa["abc"] = 321;
44  *    oaa["k"]   = 4783;
45  *    oaa["abcdef"] = 999;
46  *
47  *    // Free the pointer of keys
48  *    oaa.free;
49  *  }
50  */
51 
52 import std.string,
53        std.conv;
54 import core.memory;
55 import kontainer.linkedlist;
56 
57 struct Pair(KeyType, ValueType) {
58   KeyType key;
59   ValueType value;
60 }
61 
62 class OrderedAssocArray(KeyType, ValueType) {
63   LinkedList!(KeyType*) _keys;
64   private ValueType[KeyType] assoc;
65 
66   this() {}
67 
68   this(ValueType[KeyType] asc) {
69     foreach (key, value; asc) {
70       this.add(key, value);
71     }
72   }
73 
74   this(typeof(this) at) {
75     foreach (pair; at) {
76       this.add(pair);
77     }
78   }
79 
80   void free() {
81     foreach (node; this._keys) {
82       GC.free(node.value);
83     }
84   }
85 
86   void add(Pair!(KeyType, ValueType) pair) {
87     this.add(pair.key, pair.value);
88   }
89 
90   void add(KeyType key, ValueType value) {
91     KeyType* _key;
92 
93     if (key !in this.assoc) {
94       _key  = cast(KeyType*)GC.malloc(key.sizeof, GC.BlkAttr.NO_SCAN);
95       *_key = key;
96       this._keys.append(_key);
97     }
98 
99     this.assoc[key] = value;
100   }
101 
102   @property typeof(this) save() {
103     return new typeof(this)(this);
104   }
105 
106   bool contains(KeyType key) {
107     return (key in this.assoc) ? true : false;
108   }
109 
110   @property KeyType[] keys() {
111     KeyType[] ar;
112 
113     foreach (node; _keys) {
114       ar ~= *node.value;
115     }
116 
117     return ar;
118   }
119 
120   @property ValueType[] values() {
121     ValueType[] ar;
122 
123     foreach (node; _keys) {
124       ar ~= this[*node.value];
125     }
126 
127     return ar;
128   }
129 
130   /* Assign Operator Overloadings */
131   ValueType opIndex(KeyType key) {
132     if (key in this.assoc) {
133       return this.assoc[key];
134     } else {
135       import std.conv : to;
136       if (cast(ValueType)null) {
137         return null;
138       } else {
139         throw new Error("Nu such a key : " ~ key.to!string);
140       }
141     }
142   }
143 
144   typeof(this) opBinary(string s)(typeof(this) that) if (s == "~") {
145     typeof(this) newThis = new typeof(this)(this);
146 
147     foreach (pair; that) {
148       newThis.add(pair);
149     }
150 
151     return newThis;
152   }
153 
154   void opIndexAssign(ValueType value, KeyType key) {
155     this.add(key, value);
156   }
157 
158   @property size_t length() {
159     return this._keys.length;
160   }
161 
162   @property bool empty() {
163     return this._keys.empty;
164   }
165 
166   @property Pair!(KeyType, ValueType) front() {
167     KeyType key = *(this._keys.front.value);
168     return Pair!(KeyType, ValueType)(key, this.assoc[key]);
169   }
170 
171   @property void popFront() {
172     this._keys.popFront;
173   }
174 
175   @property override string toString() {
176     string[] strs;
177 
178     foreach (elem; this) {
179       strs ~= elem.key.to!string ~ ":" ~ elem.value.to!string;
180     }
181 
182     return "[" ~ strs.join(", ") ~ "]";
183   }
184 }