byKeyValue

@safe @trusted
byKeyValue
(
SomeMap
)
(
auto ref return SomeMap c
)
if (
is(SomeMap == HybridHashMap!(_),
_...
) &&
SomeMap.hasValue
)

Return Value

Type: auto

range that iterates through the key-value-pairs of c in undefined order.

Examples

make range from l-value and r-value. element access is always const

1 import core.exception : AssertError;
2 import std.typecons : Nullable;
3 import nxt.digest.fnv : FNV;
4 import nxt.array_help : s;
5 debug import std.exception : assertThrown;
6 
7 import std.algorithm.searching : count;
8 alias K = Nullable!(uint, uint.max);
9 alias X = HybridHashSet!(K, FNV!(64, true), defaultKeyEqualPredOf!K, Mallocator, Options(GrowOnlyFlag.no, BorrowCheckFlag.yes));
10 
11 auto k11 = K(11);
12 auto k22 = K(22);
13 auto k33 = K(33);
14 auto ks = [k11, k22, k33].s;
15 auto k44 = K(44);
16 
17 // mutable
18 auto x = X.withElements(ks);
19 assert(!x.contains(k44));
20 assert(!x.containsUsingLinearSearch(k44));
21 assert(x.length == 3);
22 
23 assert(x.byElement.count == x.length);
24 foreach (e; x.byElement)	// from l-value
25 {
26 	debug static assert(is(typeof(e) == const(K))); // always const access
27 
28 	// range invalidation forbidden:
29 	debug
30 	{
31 		assertThrown!AssertError(x.reserveExtra(1));  // range invalidation
32 		assertThrown!AssertError(x.clear());		  // range invalidation
33 		assertThrown!AssertError(x.insert(k11));	  // range invalidation
34 		assertThrown!AssertError(x.insertN([k11].s)); // range invalidation
35 		assertThrown!AssertError(x.remove(k11));	  // range invalidation
36 	}
37 
38 	// allowed
39 	assert(x.contains(e));
40 	assert(x.containsUsingLinearSearch(e));
41 
42 	const eHit = e in x;
43 	assert(eHit);		   // found
44 	assert(*eHit is e);	 // and the value equals what we searched for
45 
46 	const eDup = x.dup;	 // duplication is `const` and allowed
47 	assert(eDup == x);
48 }
49 
50 // const
51 const y = X.withElements(ks);
52 assert(!x.contains(k44));
53 assert(!x.containsUsingLinearSearch(k44));
54 foreach (e; y.byElement)	// from l-value
55 {
56 	auto _ = y.byElement;   // ok to read-borrow again
57 	assert(y.contains(e));
58 	assert(y.containsUsingLinearSearch(e));
59 	debug static assert(is(typeof(e) == const(K)));
60 }
61 
62 foreach (e; X.withElements([K(11)].s).byElement) // from r-value
63 {
64 	assert(e == K(11));
65 	debug static assert(is(typeof(e) == const(K))); // always const access
66 }

range checking

import core.exception : RangeError;
import std.typecons : Nullable;
import nxt.digest.fnv : FNV;
debug import std.exception : assertThrown, assertNotThrown;
immutable n = 11;

alias K = Nullable!(uint, uint.max);
alias V = uint;

alias X = HybridHashMap!(K, V, FNV!(64, true));

auto s = X.withCapacity(n);

void dummy(ref V value) {}

debug assertThrown!RangeError(dummy(s[K(0)]));

foreach (immutable i; 0 .. n) {
	const k = K(i);
	s[k] = V(i);
	debug assertNotThrown!RangeError(dummy(s[k]));
}

foreach (immutable i; 0 .. n) {
	const k = K(i);
	assert(s.remove(k));
	debug assertThrown!RangeError(dummy(s[k]));
}

s[K(0)] = V.init;
auto vp = K(0) in s;
debug static assert(is(typeof(vp) == V*));
assert((*vp) == V.init);

assert(s.remove(K(0)));
assert(K(0) !in s);

X t;
t.reserveExtra(4096);

t.clear();

class as value

import core.exception : RangeError;
import std.typecons : Nullable;
debug import std.exception : assertThrown, assertNotThrown;
import nxt.digest.fnv : FNV;

immutable n = 11;

alias K = Nullable!(uint, uint.max);
class V
{
	this(uint data) { this.data = data; }
	uint data;
}

alias X = HybridHashMap!(K, V, FNV!(64, true));

auto s = X.withCapacity(n);

void dummy(ref V value) {}

debug assertThrown!RangeError(dummy(s[K(0)]));

foreach (immutable i; 0 .. n) {
	const k = K(i);
	s[k] = new V(i);
	debug assertNotThrown!RangeError(dummy(s[k]));
}

// test range
{
	auto sr = s.byKeyValue; // scoped range
	assert(sr.length == n);
	foreach (immutable i; 0 .. n) {
		sr.popFront();
		assert(sr.length == n - i - 1);
	}
}

foreach (immutable i; 0 .. n) {
	const k = K(i);
	assert(s.remove(k));
	debug assertThrown!RangeError(dummy(s[k]));
}

s[K(0)] = V.init;
auto vp = K(0) in s;
debug static assert(is(typeof(vp) == V*));

assert(s.remove(K(0)));
assert(K(0) !in s);

X t;
t.reserveExtra(4096);

constness inference of ranges

import std.typecons : Nullable;
import nxt.digest.fnv : FNV;

alias K = Nullable!(uint, uint.max);
class V
{
	this(uint data) { this.data = data; }
	uint data;
}

alias X = HybridHashMap!(K, V, FNV!(64, true));
const x = X();

foreach (const e; x.byKey) {
	debug static assert(is(typeof(e) == const(X.KeyType)));
}

foreach (const e; x.byValue) {
	debug static assert(is(typeof(e) == const(X.ValueType)));
}

foreach (const e; X.init.byValue) {
	debug static assert(is(typeof(e) == const(X.ValueType)));
}

foreach (const e; x.byKeyValue) {
	debug static assert(is(typeof(e.key) == const(X.KeyType)));
	debug static assert(is(typeof(e.value) == const(X.ValueType)));
	debug static assert(is(typeof(e) == const(X.ElementType)));
}

range key constness and value mutability with class value

import std.typecons : Nullable;
import nxt.digest.fnv : FNV;

struct S
{
	uint value;
}
alias K = Nullable!(S, S(uint.min)); // use uint.min to trigger use of faster `allocator.allocateZeroed`

class V
{
	this(uint data) { this.data = data; }
	uint data;
}

alias X = HybridHashMap!(K, V, FNV!(64, true));
auto x = X();

x[K(S(42))] = new V(43);

assert(x.length == 1);

foreach (e; x.byValue)	  // `e` is auto ref
{
	debug static assert(is(typeof(e) == X.ValueType)); // mutable access to value
	assert(e.data == 43);

	// value mutation side effects
	e.data += 1;
	assert(e.data == 44);
	e.data -= 1;
	assert(e.data == 43);
}

foreach (ref e; x.byKeyValue)   // `e` is auto ref
{
	debug static assert(is(typeof(e.key) == const(X.KeyType))); // const access to key
	debug static assert(is(typeof(e.value) == X.ValueType)); // mutable access to value

	assert(e.key.value == 42);
	assert(e.value.data == 43);

	// key cannot be mutated
	debug static assert(!__traits(compiles, { e.key.value += 1; }));

	// value mutation side effects
	e.value.data += 1;
	assert(e.value.data == 44);
	e.value.data -= 1;
	assert(e.value.data == 43);
}

range key constness and value mutability with class key and class value

1 import nxt.digest.fnv : FNV;
2 
3 class K
4 {
5 	this(uint value) {
6 		this.value = value;
7 	}
8 
9 	@property bool opEquals(in typeof(this) rhs) const
10 	{
11 		return value == rhs.value;
12 	}
13 
14 	uint value;
15 }
16 
17 class V
18 {
19 	this(uint data) { this.data = data; }
20 	uint data;
21 }
22 
23 alias X = HybridHashMap!(K, V, FNV!(64, true));
24 auto x = X();
25 
26 x[new K(42)] = new V(43);
27 
28 assert(x.length == 1);
29 
30 foreach (e; x.byValue)	  // `e` is auto ref
31 {
32 	debug static assert(is(typeof(e) == X.ValueType)); // mutable access to value
33 	assert(e.data == 43);
34 
35 	// value mutation side effects
36 	e.data += 1;
37 	assert(e.data == 44);
38 	e.data -= 1;
39 	assert(e.data == 43);
40 }
41 
42 foreach (ref e; x.byKeyValue)   // `e` is auto ref
43 {
44 	debug static assert(is(typeof(e.key) == X.KeyType)); // mutable access to class key
45 	debug static assert(is(typeof(e.value) == X.ValueType)); // mutable access to value
46 
47 	assert(e.key.value == 42);
48 	assert(e.value.data == 43);
49 
50 	// class key itself should not be mutable
51 	debug static assert(!__traits(compiles, { e.key = null; }));
52 
53 	// members of key can be mutated
54 	debug static assert(__traits(compiles, { e.key.value += 1; }));
55 
56 	// value mutation side effects
57 	e.value.data += 1;
58 	assert(e.value.data == 44);
59 	e.value.data -= 1;
60 	assert(e.value.data == 43);
61 }

range key constness and value mutability with class key and class value

1 import nxt.digest.fnv : FNV;
2 class K
3 {
4 	this(uint value) scope {
5 		this.value = value;
6 	}
7 	uint value;
8 }
9 
10 struct V
11 {
12 	this(uint data) { this.data = data; }
13 	this(this) @disable;
14 	uint data;
15 }
16 
17 alias X = HybridHashMap!(K, V, FNV!(64, true));
18 auto x = X();
19 
20 scope key42 = new K(42);
21 () @trusted { x[key42] = V(43); }(); // TODO: qualify `HybridHashMap.opIndexAssign` with @trusted and remove
22 
23 assert(x.length == 1);
24 
25 foreach (ref e; x.byValue)  // `e` is auto ref
26 {
27 	debug static assert(is(typeof(e) == X.ValueType)); // mutable access to value
28 	assert(e.data == 43);
29 
30 	// value mutation side effects
31 	e.data += 1;
32 	assert(e.data == 44);
33 	e.data -= 1;
34 	assert(e.data == 43);
35 }
36 
37 foreach (ref e; x.byKeyValue) // `e` is auto ref
38 {
39 	debug static assert(is(typeof(e.key) == X.KeyType)); // mutable access to class key
40 	debug static assert(is(typeof(e.value) == X.ValueType)); // mutable access to value
41 
42 	assert(e.key.value == 42);
43 	assert(e.value.data == 43);
44 
45 	// value mutation side effects
46 	e.value.data += 1;
47 	assert(e.value.data == 44);
48 	e.value.data -= 1;
49 	assert(e.value.data == 43);
50 }
51 
52 assert(x.length == 1);
53 
54 assert(x.remove(key42));
55 assert(x.length == 0);
56 
57 () @trusted { x[key42] = V(43); }(); // TODO: qualify `HybridHashMap.opIndexAssign` with @trusted and remove
58 assert(x.length == 1);

Meta