Inconsistent MappType Behavior in JS When Empty


#1

MappType (`map<string, string>’) fields behave different in Javascript depending on whether they are empty or not. Is this expected behavior, am I doing something wrong, or is there a better way?

Example type:

type MyType {
  myMap: map<string,string>
}

Then do the following:

var test1 = MyType.make();
var test2 = MyType.make({myMap: {'a':'a'}});
test1.myMap['b'] = 'b';
test2.myMap['b'] = 'b';

I would expect test1.myMap to be {'b': 'b'} and test2.myMap to be {'a':'a','b':'b'}.
But test1.myMap is empty (no entries) and test2.myMap is as expected.

In other words, treating a MappType as a Javascript object and assigning a property if it is empty does not work. It only works if it already has at least one property.

My current workaround is to do the following at the start of a function that would receive MyType as a parameter and may need to add to myMap before passing it along to another function:
if ( !myType.myMap || myType.myMap._empty ) { myType.myMap = {}; }
This works, but feels wrong.


#2

This is because your map is undefined at this stage, you have to initialize it like you did with test2 or do define your type in a way it will initialize this field for you

myMap: map<string,string> post default {}

myMap: map<string,string> = {}

Check examples of the use of post default and = https://community.c3.ai/search?q=post%20default


#3

Never knew about post default, thanks. However, it looks like that is only for Persistable types, which this is not.
Setting constant values in a type Appears to be the definitive post on the subject, and your second recommendation (myMap: map<string,string> = {}) should be the correct way.

However, initializing that way does not give an empty object {}. It actually results in {'type': 'map<string, string>'}, meaning an Object.keys call to it gives back unwanted results. To make matters worse, assignments appear to be added to the default across calls . So with = {} on the field definition:

var test1 = MyType.make();
test1.myMap; //results in {type: 'map<string, string>'}
test1.myMap['b'] = 'b';
test1.myMap; //now gives {type: 'map<string, string>', b: 'b'}
var test2 = MyType.make();
test2.myMap; //for some unexpected reason also gives {type: 'map<string, string>', b: 'b'}
test2.myMap['c'] = 'c';
var test3 = MyType.make()
test3.myMap; //not sure why, but gives {type: 'map<string, string>', b: 'b', c: 'c'}

So is no initializer and checking myMap._empty the best way?


#4

This is a weird behavior, feels like your map is a global object!

instead of using Object.keys(..), you can actually just use test.myMap.keys, I think we support underscore functions on this.


#5

In JavaScript, {} is not the same as a C3 Map, so be careful—it is a plain JavaScript Object. C3 Map instances provide specific APIs to use; don’t use the Object.prototype APIs. For example, there is a keys member function to retrieve the list of properties defined on the Map, as well as get and set functions.

You shouldn’t have to set an initializer value for the field (= {}), as all collection fields are initialized to empty collections when an object is “made”. I suspect the initializer is shared across all instances of the type.