www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to update Associative Array?

reply tastyminerals <tastyminerals gmail.com> writes:
Not sure if the `update` method got changed but I am having 
trouble with understanding it now.
I assumed it would work as easy as in Python:) Just do 
`mydic.update(dic)` or `mydic["key"].update(anotherDic)`.

The docs have the following example.

```
class C{}
C[string] aa;

C older;
C newer;
aa.update("a",
{
     newer = new C;
     return newer;
},
(ref C c)
{
     older = c;
     newer = new C;
     return newer;
});
```

This looks pretty scary and confusing to me tbo. Also, why is 
there an example with class and not simple `int[string]`? I just 
need to know how can I update let's say `int[string]` or nested 
`int[string][string]` AA. Should I also initialise and additional 
C classes before calling this method as in the example?

Considering this is the only example in the docs I could find on 
how to update AA, imagine someone from Python world comes and 
sees this. Next thing he does, is close the page and never come 
back to D again :(
Feb 10 2022
parent reply bauss <jj_1337 live.dk> writes:
On Thursday, 10 February 2022 at 10:59:17 UTC, tastyminerals 
wrote:
 Not sure if the `update` method got changed but I am having 
 trouble with understanding it now.
 I assumed it would work as easy as in Python:) Just do 
 `mydic.update(dic)` or `mydic["key"].update(anotherDic)`.

 The docs have the following example.

 ```
 class C{}
 C[string] aa;

 C older;
 C newer;
 aa.update("a",
 {
     newer = new C;
     return newer;
 },
 (ref C c)
 {
     older = c;
     newer = new C;
     return newer;
 });
 ```

 This looks pretty scary and confusing to me tbo. Also, why is 
 there an example with class and not simple `int[string]`? I 
 just need to know how can I update let's say `int[string]` or 
 nested `int[string][string]` AA. Should I also initialise and 
 additional C classes before calling this method as in the 
 example?

 Considering this is the only example in the docs I could find 
 on how to update AA, imagine someone from Python world comes 
 and sees this. Next thing he does, is close the page and never 
 come back to D again :(
You can just do: aa[key] = value. If the key exist then it's updated, if it doesn't then it's added. The reason for the update function is simply in cases where you want to use the oldvalue etc. perhaps it has a timestamp that you need to keep etc. But in general you shouldn't need it.
Feb 10 2022
parent reply tastyminerals <tastyminerals gmail.com> writes:
On Thursday, 10 February 2022 at 12:04:04 UTC, bauss wrote:
 On Thursday, 10 February 2022 at 10:59:17 UTC, tastyminerals 
 wrote:
 [...]
You can just do: aa[key] = value. If the key exist then it's updated, if it doesn't then it's added. The reason for the update function is simply in cases where you want to use the oldvalue etc. perhaps it has a timestamp that you need to keep etc. But in general you shouldn't need it.
I meant a different thing though. I am looking for `mydic.update(another_dic)` analogue where `{"a": 1, "b": 2}` update `{"c": 3, "a": -1}` becomes `{"a":-1, "b": 2, "c": 3}`.
Feb 10 2022
parent reply novice2 <sorry noem.ail> writes:
On Thursday, 10 February 2022 at 12:08:07 UTC, tastyminerals 
wrote:
 I meant a different thing though. I am looking for 
 `mydic.update(another_dic)` analogue where `{"a": 1, "b": 2}` 
 update `{"c": 3, "a": -1}` becomes `{"a":-1, "b": 2, "c": 3}`.
you need "merge"? https://forum.dlang.org/post/fhhuupczjnhehxpljxrj forum.dlang.org
Feb 10 2022
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 2/10/22 05:42, novice2 wrote:
 On Thursday, 10 February 2022 at 12:08:07 UTC, tastyminerals wrote:
 I meant a different thing though. I am looking for
 `mydic.update(another_dic)` analogue where `{"a": 1, "b": 2}` update
 `{"c": 3, "a": -1}` becomes `{"a":-1, "b": 2, "c": 3}`.
you need "merge"? https://forum.dlang.org/post/fhhuupczjnhehxpljxrj forum.dlang.org
Probably but Python's update updates the original dictionary. I came up with the following function: import std.traits : isAssociativeArray, isImplicitlyConvertible, KeyType, ValueType; void update(Target, From)(ref Target target, From from) if (isAssociativeArray!Target && isAssociativeArray!From && isImplicitlyConvertible!(KeyType!From, KeyType!Target) && isImplicitlyConvertible!(ValueType!From, ValueType!Target)) { foreach (kv; from.byKeyValue) { target[kv.key] = kv.value; } } unittest { auto a = [ "a": 1, "b": 2 ]; a.update([ "c": 3, "a": -1 ]); assert(a == [ "a": -1, "b": 2, "c": 3 ]); } void main() { } Yes, it may look scary to newcomers to D but the template constraint is just for improved usability. (Still, I suspect it's not complete.) Otherwise, the following is the same thing: void update(Target, From)(ref Target target, From from) { foreach (kv; from.byKeyValue) { target[kv.key] = kv.value; } } 'ref' is needed there to work with null AAs. Without 'ref', the following would not work: int[string] aa; aa.update([ "x" : 42 ]); 'aa' would be null! Welcome to AA reference semantics. :) Ali
Feb 10 2022
parent reply kdevel <kdevel vogtner.de> writes:
On Thursday, 10 February 2022 at 17:09:23 UTC, Ali Çehreli wrote:
 import std.traits : isAssociativeArray, 
 isImplicitlyConvertible, KeyType, ValueType;

 void update(Target, From)(ref Target target, From from)
 if (isAssociativeArray!Target &&
     isAssociativeArray!From &&
     isImplicitlyConvertible!(KeyType!From, KeyType!Target) &&
     isImplicitlyConvertible!(ValueType!From, ValueType!Target))
 {
   foreach (kv; from.byKeyValue) {
     target[kv.key] = kv.value;
   }
 }
[...]
 Yes, it may look scary to newcomers to D but the template 
 constraint is just for improved usability.
It also looks scary to me and I use D now for quite a while. Assume I have this client code: string[int] q; byte[short] r; q.update (r); It produces this error message from your version: $ dmd -checkaction=context -unittest -run v1 v1.d(21): Error: template `v1.update` cannot deduce function from argument types `!()(string[int], byte[short])` v1.d(3): Candidate is: `update(Target, From)(ref Target target, From from)` with `Target = string[int], From = byte[short]` must satisfy the following constraint: ` isImplicitlyConvertible!(ValueType!From, ValueType!Target)` If I remove the constraint (the if-stuff) I get v1.d(12): Error: cannot implicitly convert expression `kv.value()` of type `byte` to `string` v1.d(23): Error: template instance `v1.update!(string[int], byte[short])` error instantiating Can this really be improved? Stefan
Feb 12 2022
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 2/12/22 13:37, kdevel wrote:
 On Thursday, 10 February 2022 at 17:09:23 UTC, Ali Çehreli wrote:
 import std.traits : isAssociativeArray, isImplicitlyConvertible,
 KeyType, ValueType;

 void update(Target, From)(ref Target target, From from)
 if (isAssociativeArray!Target &&
     isAssociativeArray!From &&
     isImplicitlyConvertible!(KeyType!From, KeyType!Target) &&
     isImplicitlyConvertible!(ValueType!From, ValueType!Target))
 {
   foreach (kv; from.byKeyValue) {
     target[kv.key] = kv.value;
   }
 }
[...]
 Yes, it may look scary to newcomers to D but the template constraint
 is just for improved usability.
It also looks scary to me and I use D now for quite a while. Assume I have this client code: string[int] q; byte[short] r; q.update (r);
So, that code is not valid because a 'byte' cannot implicitly be converted to a 'string'.
 It produces this error message from your version:

     $ dmd -checkaction=context -unittest -run v1
     v1.d(21): Error: template `v1.update` cannot deduce function from
 argument types `!()(string[int], byte[short])`
     v1.d(3):        Candidate is: `update(Target, From)(ref Target
 target, From from)`
       with `Target = string[int],
            From = byte[short]`
       must satisfy the following constraint:
     `       isImplicitlyConvertible!(ValueType!From, ValueType!Target)`
That looks like a wall of text but is much better than before. Now, the compiler is telling us what requirement could not be met. Additionally, the compilation fail in user code; telling the user why their code is not valid.
 If I remove the constraint (the if-stuff) I get

     v1.d(12): Error: cannot implicitly convert expression `kv.value()`
 of type `byte` to `string`
     v1.d(23): Error: template instance `v1.update!(string[int],
 byte[short])` error instantiating
That's inferior because now the error message is pointing at the implementation of a library function and confuses everyone. We have such functions in Phobos.
 Can this really be improved?
I am not sure. Here is an idea, which doesn't seem to add much as is: 'if' constraints could have a message for an improved error message like "This function template could not be used because 'byte' cannot implicitly be converted to 'string'." Note that there could be many candidates like "And this function template could not be used because 42 is not prime", etc. The programmer would read all those and figure out usable parameters. But I think those would just be translations of the error messages. Another idea is perhaps the compiler could resolve ValueType!From to 'byte' and provide the more readable isImplicitlyConvertible!(byte, string) I don't know how feasible that is. Still, I am glad the error messages today are much better. After all, everything that the programmer needs is spelled out here. It requires following some types: with `Target = string[int], From = byte[short]` must satisfy the following constraint: isImplicitlyConvertible!(ValueType!From, ValueType!Target)` The only thing that's not readily available there is ValueType but at least it's very readable. Sorry, I rambled but I'm under the impression that this complexity is inherent. Ali
Feb 12 2022
parent reply kdevel <kdevel vogtner.de> writes:
On Sunday, 13 February 2022 at 01:27:45 UTC, Ali Çehreli wrote:
 [...]
 If I remove the constraint (the if-stuff) I get

     v1.d(12): Error: cannot implicitly convert expression
`kv.value()`
 of type `byte` to `string`
     v1.d(23): Error: template instance
`v1.update!(string[int],
 byte[short])` error instantiating
That's inferior because now the error message is pointing at the implementation of a library function and confuses everyone.
It confuses me not really. (Function) templates are by their very nature visible to the user and I got used to reading the implementations. Nonetheless may the compiler-generated checks be moved into the interface: void update(Target, From)(ref Target target, From from, Target dummy = From.init) For this function template dmd reports in case of incompatible types: cannot implicitly convert expression `null` of type `byte[short]` to `string[int]`
 [...]
 After all, everything that the programmer needs is spelled out 
 here. It requires following some types:


       with `Target = string[int],
            From = byte[short]`
       must satisfy the following constraint:
              isImplicitlyConvertible!(ValueType!From, 
 ValueType!Target)`

 The only thing that's not readily available there is ValueType 
 but at least it's very readable.
The constraints appear artificial to me: Neither are there means which ensure that a list of Constraints is complete nor is it ensured that a list of constraints is not overly restrictive. It seems that these Constraints constitute another class of things which may get out of sync with the actual code (besides program documentation, program comments and function names). Doesn't this re-iteration of what is already implicitly contained in the line target[kv.key] = kv.value; violate the DRY principle?
Feb 13 2022
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 2/13/22 05:58, kdevel wrote:
 On Sunday, 13 February 2022 at 01:27:45 UTC, Ali Çehreli wrote:
 [...]
 If I remove the constraint (the if-stuff) I get

     v1.d(12): Error: cannot implicitly convert expression
`kv.value()`
 of type `byte` to `string`
     v1.d(23): Error: template instance
`v1.update!(string[int],
 byte[short])` error instantiating
That's inferior because now the error message is pointing at the implementation of a library function and confuses everyone.
It confuses me not really. (Function) templates are by their very nature visible to the user and I got used to reading the implementations.
Point taken but how deep should we understand library code, not all being Phobos. My pet peeve: import std.stdio; void main(string[] args) { writefln!"hello"(42); } /usr/include/dlang/dmd/std/stdio.d(4442): Error: no property `msg` for type `string` I am happy that it's caught at compile time but what 'msg' property are we talking about? What does it have to be with me? I would like it if the It would be better if the compilation error said: Must satify formatSpecsMatchArgs!("hello", int) I am not sure whether it's practical or efficient at compile time.
 The constraints appear artificial to me: Neither are there means which
 ensure
 that a list of Constraints is complete nor is it ensured that a list of
 constraints is not overly restrictive.
Agreed.
 It seems that these Constraints
 constitute
 another class of things which may get out of sync with the actual code
 (besides program documentation, program comments and function names).
Agreed.
 Doesn't this re-iteration of what is already implicitly contained in the
 line

     target[kv.key] = kv.value;
I don't agree with that because there is no 'target', 'kv', etc. in the programmer's code. Should we really expect the programmers be programmers? :p
 violate the DRY principle?
Agreed. Ali
Feb 13 2022
parent kdevel <kdevel vogtner.de> writes:
On Monday, 14 February 2022 at 01:04:08 UTC, Ali Çehreli wrote:

[...]

 Point taken but how deep should we understand library code, not 
 all being Phobos. My pet peeve:

 import std.stdio;

 void main(string[] args) {
   writefln!"hello"(42);
 }

 /usr/include/dlang/dmd/std/stdio.d(4442): Error: no property 
 `msg` for type `string`

 I am happy that it's caught at compile time but what 'msg' 
 property are we talking about? What does it have to be with me?

 I would like it if the

 It would be better if the compilation error said:

   Must satify formatSpecsMatchArgs!("hello", int)
Interestingly writefln ("hello", 42); does not throw at runtime.
Feb 18 2022