Thread overview
Empty non-null Associative Arrays should be trivial or even the default.
Aug 03, 2021
Rekel
Aug 03, 2021
Mike Parker
Aug 03, 2021
Rekel
Aug 03, 2021
Rekel
Aug 03, 2021
Mike Parker
August 03, 2021

Having to insert dummy variables into an associative array in order to initialize it (and prevent foreach loops from throwing a range violation error) seems very dirty to me. Why is there no syntax for empty initialization?

Suddenly getting a nullpointer after removing a ~= in an entirely different part of my code is 1 (VERY problematic) thing, but I'm even more surprised by the possible 'fixes'. . . (which would be either a useless insertion & removal procedure or a nullpointer check I will never again need after initial steps.)

Is there any plan of resolving this issue? I'm shocked this is even a thing at all.

August 03, 2021

On Tuesday, 3 August 2021 at 15:51:43 UTC, Rekel wrote:

>

Having to insert dummy variables into an associative array in order to initialize it (and prevent foreach loops from throwing a range violation error) seems very dirty to me. Why is there no syntax for empty initialization?

Associative arrays are initialized as empty by default. The following compiles and prints nothing, as expected:

int[string] aa;
foreach(k, v; aa) writeln(v);

What are you doing that causes the range violation?

>

Suddenly getting a nullpointer after removing a ~= in an entirely different part of my code is 1 (VERY problematic) thing, but I'm even more surprised by the possible 'fixes'. . . (which would be either a useless insertion & removal procedure or a nullpointer check I will never again need after initial steps.)

You mean, doing something like aa["foo"] gives you the range violation? That's intended. If the key was never assigned, it's just like trying to index a normal array with a value that's out of bounds. The proper way to check for an element in an AA is via in, which returns a pointer to the element if it exists and null if it doesn't:

int[string] aa;
if(auto v = "foo" in aa) writeln(*v);
else writeln("No foo.");

See:
https://dlang.org/spec/hash-map.html#testing_membership

August 03, 2021

Feel free to disregard my previous post seems valid, as I seem to have misunderstood the meaning of AA's being null. (Wish I'd triple checked, it was an incorrect use of ) Sorry for wasting anyone's time...

How can all properties still be callable with a null-AA? This surprises me.

Also a small sidequestion; how come remove is part of AA's definition while the removal of items from dynamic lists is part of the library instead?


https://tenor.com/view/justin-timberlake-jt-bad-teacher-stupid-ifeel-stupid-gif-3547095

For those interested, my range error was caused by the following mistake;

int[][int] aa;

void addElement(int element) {
	aa[0] ~= element;
}

void main(string[] args) { // Works
	addElement(0);
	aa[0].writeln();
}

void main(string[] args) { // Does not work
	aa[0].writeln();
}

Assumptions thus on present elements, although the first main does not allude to its absence. In particular, why can one ~= when aa[0] does not exist?
Likewise, why is this valid:?

int[] a;
assert(a is null); // Strange to me this works as many have said dynamic arrays are structs.
a ~= 1;

writeln(a);
writeln([] ~ 1);
// But not writeln(null ~ 1);

Again sorry for my previous mistake. I'll be more careful in the future.

  • Paul
August 03, 2021

On Tuesday, 3 August 2021 at 16:20:50 UTC, Mike Parker wrote:

>

You mean, doing something like aa["foo"] gives you the range violation? That's intended. If the key was never assigned, it's just like trying to index a normal array with a value that's out of bounds. The proper way to check for an element in an AA is via in, which returns a pointer to the element if it exists and null if it doesn't:

Yes, you are absolutely correct. You've been faster at responding than I have been at backtracking my previous statements, that is very impressive 😅, thanks for helping out.

August 03, 2021

On Tuesday, 3 August 2021 at 16:28:03 UTC, Rekel wrote:

>

Also a small sidequestion; how come remove is part of AA's definition while the removal of items from dynamic lists is part of the library instead?

AAs have to do internal bookkeeping, with the buckets and such, that dynamic arrays do not have to worry about. So they've had their own remove function from the beginning.

As I recall, dynamic arrays didn't have a remove function back in the D1 days. Removal was a matter of iteration and slicing:

foreach(i, e; arr)
{
    if(e == toRemove)
    {
        arr = a[0 .. i] ~ a[i+1 .. $];
        break;
    }
}

And the remove function we have now is not specifically for dynamic arrays. It's for ranges. Dynamic arrays happen to be ranges (courtesy of the range API implemented for them in std.array) so you can use them with that function and any function in std.algorithm.

Static arrays and associative arrays are not ranges.

August 03, 2021

On 8/3/21 12:28 PM, Rekel wrote:

>

Feel free to disregard my previous post seems valid, as I seem to have misunderstood the meaning of AA's being null. (Wish I'd triple checked, it was an incorrect use of ) Sorry for wasting anyone's time...

How can all properties still be callable with a null-AA? This surprises me.

An AA is really a struct with a pointer to an implementation struct. Therefore, a null AA is still valid, it just has a null implementation (which is treated as an empty one).

For example, AA length property looks like:

size_t aaLength(AA *impl) {
   if(impl is null) return 0;
   return impl.length;
}

or something along those lines.

>

Also a small sidequestion; how come remove is part of AA's definition while the removal of items from dynamic lists is part of the library instead?

A remove of an AA is an amortized O(1) operation, which makes it fast enough to be a builtin property.

Removal from a dynamic array involves shifting elements around, and is O(n), so it's left up to a library to do if it wishes.

>

https://tenor.com/view/justin-timberlake-jt-bad-teacher-stupid-ifeel-stupid-gif-3547095

For those interested, my range error was caused by the following mistake;

int[][int] aa;

void addElement(int element) {
     aa[0] ~= element;
}

void main(string[] args) { // Works
     addElement(0);
     aa[0].writeln();
}

void main(string[] args) { // Does not work
     aa[0].writeln();
}

AA's have a special behavior depending on whether your indexed value is used as an rvalue or lvalue.

So for instance writeln(aa[0]) is using aa[0] as an rvalue, so no dummy element is inserted. However, aa[0] ~= element requires an lvalue, so one is added if it doesn't exist.

-Steve