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 
19 // ***************************************************************************
20 
21 /// Get a value from an AA, and throw an exception (not an error) if not found
22 ref V aaGet(K, V)(V[K] aa, K key)
23 {
24 	import std.conv;
25 
26 	auto p = key in aa;
27 	if (p)
28 		return *p;
29 	else
30 		static if (is(typeof(text(key))))
31 			throw new Exception("Absent value: " ~ text(key));
32 		else
33 			throw new Exception("Absent value");
34 }
35 
36 /// If key is not in aa, add it with defaultValue.
37 /// Returns a reference to the value corresponding to key.
38 ref V getOrAdd(K, V)(ref V[K] aa, K key, V defaultValue = V.init)
39 {
40 	auto p = key in aa;
41 	if (!p)
42 	{
43 		aa[key] = defaultValue;
44 		p = key in aa;
45 	}
46 	return *p;
47 }
48 
49 unittest
50 {
51 	int[int] aa;
52 	aa.getOrAdd(1, 2) = 3;
53 	assert(aa[1] == 3);
54 	assert(aa.getOrAdd(1, 4) == 3);
55 }
56 
57 struct KeyValuePair(K, V) { K key; V value; }
58 
59 /// Get key/value pairs from AA
60 KeyValuePair!(K, V)[] pairs(K, V)(V[K] aa)
61 {
62 	KeyValuePair!(K, V)[] result;
63 	foreach (key, value; aa)
64 		result ~= KeyValuePair!(K, V)(key, value);
65 	return result;
66 }
67 
68 /// Get key/value pairs from AA, sorted by keys
69 KeyValuePair!(K, V)[] sortedPairs(K, V)(V[K] aa)
70 {
71 	KeyValuePair!(K, V)[] result;
72 	foreach (key; aa.keys.sort)
73 		result ~= KeyValuePair!(K, V)(key, aa[key]);
74 	return result;
75 }
76 
77 /// Get values from AA, sorted by keys
78 V[] sortedValues(K, V)(V[K] aa)
79 {
80 	V[] result;
81 	foreach (key; aa.keys.sort)
82 		result ~= aa[key];
83 	return result;
84 }
85 
86 /// Merge source into target. Return target.
87 V[K] merge(K, V)(auto ref V[K] target, in V[K] source)
88 {
89 	foreach (k, v; source)
90 		target[k] = v;
91 	return target;
92 }
93 
94 unittest
95 {
96 	int[int] target;
97 	int[int] source = [2:4];
98 	merge(target, source);
99 	assert(source == target);
100 
101 	target = [1:1, 2:2, 3:3];
102 	merge(target, source);
103 	assert(target == [1:1, 2:4, 3:3]);
104 
105 	assert(merge([1:1], [2:2]) == [1:1, 2:2]);
106 }
107 
108 // ***************************************************************************
109 
110 /// An associative array which retains the order in which elements were added.
111 struct OrderedMap(K, V)
112 {
113 	K[] keys;
114 	V[] values;
115 	size_t[K] index;
116 
117 	ref V opIndex(ref K k)
118 	{
119 		return values[index[k]];
120 	}
121 
122 	ref V opIndexAssign()(auto ref V v, auto ref K k)
123 	{
124 		auto pi = k in index;
125 		if (pi)
126 		{
127 			auto pv = &values[*pi];
128 			*pv = v;
129 			return *pv;
130 		}
131 
132 		index[k] = values.length;
133 		keys ~= k;
134 		values ~= v;
135 		return values[$-1];
136 	}
137 
138 	void remove()(auto ref K k)
139 	{
140 		auto i = index[k];
141 		index.remove(k);
142 		keys = keys.remove(i);
143 		values = values.remove(i);
144 	}
145 
146 	@property size_t length() { return values.length; }
147 
148 	int opApply(int delegate(ref K k, ref V v) dg)
149 	{
150 		int result = 0;
151 
152 		foreach (i, ref v; values)
153 		{
154 			result = dg(keys[i], v);
155 			if (result)
156 				break;
157 		}
158 		return result;
159 	}
160 }
161 
162 unittest
163 {
164 	OrderedMap!(string, int) m;
165 	m["a"] = 1;
166 	m["b"] = 2;
167 	m["c"] = 3;
168 	assert(m.length == 3);
169 	m.remove("a");
170 	assert(m.length == 2);
171 }
172 
173 // ***************************************************************************
174 
175 /// Helper/wrapper for void[0][T]
176 struct HashSet(T)
177 {
178 	void[0][T] data;
179 
180 	alias data this;
181 
182 	this(R)(R r)
183 	{
184 		foreach (k; r)
185 			add(k);
186 	}
187 
188 	void add(T k)
189 	{
190 		void[0] v;
191 		data[k] = v;
192 	}
193 
194 	void remove(T k)
195 	{
196 		data.remove(k);
197 	}
198 
199 	@property HashSet!T dup() const
200 	{
201 		// Can't use .dup with void[0] value
202 		HashSet!T result;
203 		foreach (k, v; data)
204 			result.add(k);
205 		return result;
206 	}
207 
208 	int opApply(scope int delegate(ref T) dg)
209 	{
210 		int result;
211 		foreach (k, v; data)
212 			if ((result = dg(k)) != 0)
213 				break;
214 		return result;
215 	}
216 }
217 
218 unittest
219 {
220 	HashSet!int s;
221 	assert(s.length == 0);
222 	assert(!(1 in s));
223 	assert(1 !in s);
224 	s.add(1);
225 	assert(1 in s);
226 	assert(s.length == 1);
227 	foreach (k; s)
228 		assert(k == 1);
229 	s.remove(1);
230 	assert(s.length == 0);
231 
232 	s.add(1);
233 	auto t = s.dup;
234 	s.add(2);
235 	assert(t.length==1);
236 	t.remove(1);
237 	assert(t.length==0);
238 }
239 
240 auto toSet(R)(R r)
241 {
242 	alias E = ElementType!R;
243 	return HashSet!E(r);
244 }
245 
246 unittest
247 {
248 	auto set = [1, 2, 3].toSet();
249 	assert(2 in set);
250 	assert(4 !in set);
251 }