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 	ref inout(V) opIndex()(auto ref K k) inout
184 	{
185 		return values[index[k]];
186 	}
187 
188 	ref V opIndexAssign()(auto ref V v, auto ref K k)
189 	{
190 		auto pi = k in index;
191 		if (pi)
192 		{
193 			auto pv = &values[*pi];
194 			*pv = v;
195 			return *pv;
196 		}
197 
198 		index[k] = values.length;
199 		keys ~= k;
200 		values ~= v;
201 		return values[$-1];
202 	}
203 
204 	ref V getOrAdd()(auto ref K k)
205 	{
206 		auto pi = k in index;
207 		V* pv;
208 		if (pi)
209 			pv = &values[*pi];
210 		else
211 		{
212 			index[k] = values.length;
213 			keys ~= k;
214 			values ~= V.init;
215 			pv = &values[$-1];
216 		}
217 		return *pv;
218 	}
219 
220 	ref V opIndexUnary(string op)(auto ref K k)
221 	{
222 		auto pv = &getOrAdd(k);
223 		mixin("(*pv) " ~ op ~ ";");
224 		return *pv;
225 	}
226 
227 	ref V opIndexOpAssign(string op)(auto ref V v, auto ref K k)
228 	{
229 		auto pv = &getOrAdd(k);
230 		mixin("(*pv) " ~ op ~ "= v;");
231 		return *pv;
232 	}
233 
234 	inout(V) get()(auto ref K k, inout(V) defaultValue) inout
235 	{
236 		auto p = k in index;
237 		return p ? values[*p] : defaultValue;
238 	}
239 
240 	inout(V)* opIn_r()(auto ref K k) inout
241 	{
242 		auto p = k in index;
243 		return p ? &values[*p] : null;
244 	}
245 
246 	void remove()(auto ref K k)
247 	{
248 		auto i = index[k];
249 		index.remove(k);
250 		keys = keys.remove(i);
251 		values = values.remove(i);
252 		foreach (key, ref idx; index)
253 			if (idx > i)
254 				idx--;
255 	}
256 
257 	@property size_t length() const { return values.length; }
258 
259 	private int opApplyImpl(this This, Dg)(Dg dg)
260 	{
261 		int result = 0;
262 
263 		foreach (i, ref v; values)
264 		{
265 			result = dg(keys[i], v);
266 			if (result)
267 				break;
268 		}
269 		return result;
270 	}
271 
272 	int opApply(int delegate(ref K k, ref V v) dg)
273 	{
274 		return opApplyImpl(dg);
275 	}
276 
277 	int opApply(int delegate(const ref K k, const ref V v) dg) const
278 	{
279 		return opApplyImpl(dg);
280 	}
281 
282 	@property typeof(this) dup()
283 	{
284 		typeof(this) result;
285 		result.keys = keys.dup;
286 		result.values = values.dup;
287 		result.index = index.dup;
288 		return result;
289 	}
290 }
291 
292 unittest
293 {
294 	OrderedMap!(string, int) m;
295 	m["a"] = 1;
296 	m["b"] = 2;
297 	m["c"] = 3;
298 	assert(m.length == 3);
299 	assert("a" in m);
300 	assert("d" !in m);
301 	m.remove("a");
302 	assert(m.length == 2);
303 	m["x"] -= 1;
304 	assert(m["x"] == -1);
305 	++m["y"];
306 	assert(m["y"] == 1);
307 	auto cm = cast(const)m.dup;
308 	foreach (k, v; cm)
309 		if (k == "x")
310 			assert(v == -1);
311 }
312 
313 unittest
314 {
315 	OrderedMap!(string, int) m;
316 	m["a"] = 1;
317 	m["b"] = 2;
318 	m.remove("a");
319 	assert(m["b"] == 2);
320 }
321 
322 unittest
323 {
324 	OrderedMap!(string, int) m;
325 	m["a"] = 1;
326 	auto m2 = m;
327 	m2.remove("a");
328 	m2["b"] = 2;
329 	assert(m["a"] == 1);
330 }
331 
332 unittest
333 {
334 	OrderedMap!(string, int) m;
335 	m["a"] = 1;
336 	m["b"] = 2;
337 	auto m2 = m;
338 	m.remove("a");
339 	assert(m2["a"] == 1);
340 }
341 
342 // https://issues.dlang.org/show_bug.cgi?id=18606
343 unittest
344 {
345 	struct S
346 	{
347 		struct T
348 		{
349 			int foo;
350 			int[] bar;
351 		}
352 
353 		OrderedMap!(int, T) m;
354 	}
355 }
356 
357 // ***************************************************************************
358 
359 /// Helper/wrapper for void[0][T]
360 struct HashSet(T)
361 {
362 	void[0][T] data;
363 
364 	alias data this;
365 
366 	this(R)(R r)
367 	{
368 		foreach (k; r)
369 			add(k);
370 	}
371 
372 	void add(T k)
373 	{
374 		void[0] v;
375 		data[k] = v;
376 	}
377 
378 	void remove(T k)
379 	{
380 		data.remove(k);
381 	}
382 
383 	@property HashSet!T dup() const
384 	{
385 		// Can't use .dup with void[0] value
386 		HashSet!T result;
387 		foreach (k, v; data)
388 			result.add(k);
389 		return result;
390 	}
391 
392 	int opApply(scope int delegate(ref T) dg)
393 	{
394 		int result;
395 		foreach (k, v; data)
396 			if ((result = dg(k)) != 0)
397 				break;
398 		return result;
399 	}
400 }
401 
402 unittest
403 {
404 	HashSet!int s;
405 	assert(s.length == 0);
406 	assert(!(1 in s));
407 	assert(1 !in s);
408 	s.add(1);
409 	assert(1 in s);
410 	assert(s.length == 1);
411 	foreach (k; s)
412 		assert(k == 1);
413 	s.remove(1);
414 	assert(s.length == 0);
415 
416 	s.add(1);
417 	auto t = s.dup;
418 	s.add(2);
419 	assert(t.length==1);
420 	t.remove(1);
421 	assert(t.length==0);
422 }
423 
424 auto toSet(R)(R r)
425 {
426 	alias E = ElementType!R;
427 	return HashSet!E(r);
428 }
429 
430 unittest
431 {
432 	auto set = [1, 2, 3].toSet();
433 	assert(2 in set);
434 	assert(4 !in set);
435 }
436 
437 // ***************************************************************************
438 
439 /// An object which acts mostly as an associative array,
440 /// with the added property of being able to hold keys with
441 /// multiple values. These are only exposed explicitly and
442 /// through iteration
443 struct MultiAA(K, V)
444 {
445 	V[][K] items;
446 
447 	/// If multiple items with this name are present,
448 	/// only the first one is returned.
449 	ref inout(V) opIndex(K key) inout
450 	{
451 		return items[key][0];
452 	}
453 
454 	V opIndexAssign(V value, K key)
455 	{
456 		items[key] = [value];
457 		return value;
458 	}
459 
460 	inout(V)* opIn_r(K key) inout @nogc
461 	{
462 		auto pvalues = key in items;
463 		if (pvalues && (*pvalues).length)
464 			return &(*pvalues)[0];
465 		return null;
466 	}
467 
468 	void remove(K key)
469 	{
470 		items.remove(key);
471 	}
472 
473 	// D forces these to be "ref"
474 	int opApply(int delegate(ref K key, ref V value) dg)
475 	{
476 		int ret;
477 		outer:
478 		foreach (key, values; items)
479 			foreach (ref value; values)
480 			{
481 				ret = dg(key, value);
482 				if (ret)
483 					break outer;
484 			}
485 		return ret;
486 	}
487 
488 	// Copy-paste because of https://issues.dlang.org/show_bug.cgi?id=7543
489 	int opApply(int delegate(ref const(K) key, ref const(V) value) dg) const
490 	{
491 		int ret;
492 		outer:
493 		foreach (key, values; items)
494 			foreach (ref value; values)
495 			{
496 				ret = dg(key, value);
497 				if (ret)
498 					break outer;
499 			}
500 		return ret;
501 	}
502 
503 	void add(K key, V value)
504 	{
505 		if (key !in items)
506 			items[key] = [value];
507 		else
508 			items[key] ~= value;
509 	}
510 
511 	V get(K key, lazy V def) const
512 	{
513 		auto pvalue = key in this;
514 		return pvalue ? *pvalue : def;
515 	}
516 
517 	inout(V)[] getAll(K key) inout
518 	{
519 		inout(V)[] result;
520 		foreach (ref value; items.get(key, null))
521 			result ~= value;
522 		return result;
523 	}
524 
525 	this(typeof(null) Null)
526 	{
527 	}
528 
529 	this(V[K] aa)
530 	{
531 		foreach (ref key, ref value; aa)
532 			add(key, value);
533 	}
534 
535 	this(V[][K] aa)
536 	{
537 		foreach (ref key, values; aa)
538 			foreach (ref value; values)
539 				add(key, value);
540 	}
541 
542 	@property auto keys() inout { return items.keys; }
543 
544 	// https://issues.dlang.org/show_bug.cgi?id=14626
545 
546 	@property V[] values()
547 	{
548 		return items.byValue.join;
549 	}
550 
551 	@property const(V)[] values() const
552 	{
553 		return items.byValue.join;
554 	}
555 
556 	@property typeof(V[K].init.pairs) pairs()
557 	{
558 		alias Pair = typeof(V[K].init.pairs[0]);
559 		Pair[] result;
560 		result.reserve(length);
561 		foreach (ref k, ref v; this)
562 			result ~= Pair(k, v);
563 		return result;
564 	}
565 
566 	@property size_t length() const { return items.byValue.map!(item => item.length).sum(); }
567 
568 	auto byKey() { return items.byKey(); }
569 	auto byValue() { return items.byValue().joiner(); }
570 
571 	bool opCast(T)() inout
572 		if (is(T == bool))
573 	{
574 		return !!items;
575 	}
576 
577 	/// Warning: discards repeating items
578 	V[K] opCast(T)() const
579 		if (is(T == V[K]))
580 	{
581 		V[K] result;
582 		foreach (key, value; this)
583 			result[key] = value;
584 		return result;
585 	}
586 
587 	V[][K] opCast(T)() inout
588 		if (is(T == V[][K]))
589 	{
590 		V[][K] result;
591 		foreach (k, v; this)
592 			result[k] ~= v;
593 		return result;
594 	}
595 }
596 
597 unittest
598 {
599 	MultiAA!(string, string) aa;
600 }