Update Map within Map - immutable.js

I have a immutable map object, within a Map object, as follows:
let initialState = Immutable.fromJS({errors:{}});
How do I update the errors Map so as to add and delete entries?
For example:
From:
Immutable.fromJS({errors:{}})
To:
Immutable.fromJS({errors:{"foo":"My foo error"}})
To:
Immutable.fromJS({errors:{"foo":"My foo error", "baz": "A baz error"}})
To:
Immutable.fromJS({errors:{"baz": "A baz error"}})

You can use setIn and deleteIn:
const initialState = Immutable.fromJS({errors:{}});
const nextState = initialState.setIn(['errors', 'foo'], 'bar').setIn(['errors', 'bar'], 'a bar error');
console.log(nextState.toJS());
const finalState = nextState.deleteIn(['errors', 'bar]);
console.log(finalState.toJS());
Outputs:
{
errors: {
foo: "a foo error",
bar: "a bar error"
}
}
{
errors: {
foo: "a foo error"
}
}
```

Related

Casting fails when deserializing JSON

Please take into account that this question is about Typescript and not vanilla Javascript.
I am trying to deserialize a very simple JSON string into a Typescript object and then casting into the correct type.
After casting at const obj = <FooClass>JSON.parse(s) I would expect the typeof operator to return FooClass. Why the operator still returns object ?
Why does casting here fails? How can I deserialize and still have access to somefunc ?
Example code:
class FooClass {
public baz = 0
public somefunc() {
return this.baz * 2
}
}
const jsonData = {
baz: 1234,
}
test('deserialize example', () => {
const s = JSON.stringify(jsonData)
const obj = <FooClass>JSON.parse(s) // Cast here
console.log(typeof obj) // typeof still returns object
console.log(obj)
console.log(obj.somefunc())
})
Output:
console.log
object
at Object.<anonymous> (tests/deserialize.test.ts:15:11)
console.log
{ baz: 1234 }
at Object.<anonymous> (tests/deserialize.test.ts:17:11)
TypeError: obj.somefunc is not a function
In typescript you can cast any (return type of JSON.parse) to anything. The responsibility of ensuring if the casting is "correct", and the casted value indeed matches the type it's being casted to is yours.
Casting is only telling the type checker how to treat that value from the point of casting.
Turning that object to an instance of your class is also your responsibility. You could however do something like this:
type Foo = {
baz: number
}
class FooClass {
public baz: number = 0
constructor(input: Foo) {
this.baz = input.baz
}
public somefunc() {
return this.baz * 2
}
}
const rawFoo = JSON.parse(s) as Foo
const fooClassInstance = new FooClass(rawFoo)
// ready to be used as an instance of FooClass
Playground
For completion, I copy here a solution that I find both efficient and clean:
test('casting fails example', () => {
const s = JSON.stringify(jsonData)
const obj = Object.assign(new FooClass(), JSON.parse(s))
console.log(typeof obj)
console.log(obj)
console.log(obj.somefunc())
})
This could later be improved using generics
function deserializeJSON<T>(c: { new (): T }, s: string): T {
return Object.assign(new c(), JSON.parse(s))
}
const obj = deserializeJSON(FooClass, s)

prevent replacing ES6 Object spread notation in Terser

!((
input,
processed = {
foo: 1,
...input
}
) => {
window.console.log(processed)
})({
bar: 2 // input configuration
})
gets minified to:
((t, e = {
foo: 1,
bar: 2
}) => {
window.console.log(e);
})();
I need that input parameter for later configuration
Question: How to maintain the original pattern?
Terser output I need:
((t, e = {
foo: 1,
...t
}) => {
window.console.log(e);
})({bar: 2});
Update after comment:
let input1 = { bar:2 }
!((
input,
processed = {
foo: 1,
...input
}
) => {
window.console.log(processed)
})( input1 )
outputs:
((t, e = {
foo: 1,
...t
}) => {
window.console.log(e);
})({
bar: 2
});
Terser will take care of the current version of your code. Right now, you are passing a constant parameter to a function, so terser can just inline it preventing the creation of intermediate objects.
If in the future you change this parameter inside (for primitive values) or outside the function (for objects) the function, terser should recognize that and not inline anymore.
Surprisingly the already declaring the parameter as a variable seems to give the proper hint to terser, as discovered by OP:
let input1 = { bar:2 }
!((
input,
processed = {
foo: 1,
...input
}
) => {
window.console.log(processed)
})( input1 )
will result in
((t, e = {
foo: 1,
...t
}) => {
window.console.log(e);
})({
bar: 2
});

How to set the key of a dictionary as the value of a variable?

I have a few variables which are defined as:
function test() {
var foo = 'bar'
var dict = {
foo: 'haha'
}
Logger.log(dict['foo']) // haha
Logger.log(dict[foo]) // undefined. I expected this to return 'haha'. But I was wrong.
}
Is it possible to create a dictionary that contains bar as its key using the variable foo?
I mean I want to get 'haha' by using the variable foo.
function test() {
var foo = 'bar'
var dict = {
foo: 'haha',
sec: foo
}
Logger.log(dict['foo']) // haha
Logger.log(dict[foo]) // undefined
Logger.log(dict['bar']) // undefined
Logger.log(dict['sec']) // bar - gets the variable foo
}

ES6 template literal with object

I have a function which prints an template literal:
function func() {
const obj = {
a: 1,
b: 2
};
console.log(`obj = ${obj}`);
}
It prints "obj = [object Object]".
If I want to log the content of the object(prints "obj = {a: 1, b: 2}"), how can I revise the code?
JSON stringify it.
function func() {
const obj = {
a: 1,
b: 2
};
console.log(`obj = ${JSON.stringify(obj)}`);
}
func();

Parsing JSON with Dart

I want to be able to parse a string to an object that I can access using the dot notation e.g. myobject.property, instead of the array notation e.g. myobject['property']. The array notation works fine. Here's what I have so far.
I have some XML:
<level1 name="level1name">
<level2 type="level2Type">
<entry>level2entry</entry>
<entry>level2entry</entry>
</level2>
</level1>
Which converts to the JSON:
{
"level1": {
"name": "level1name",
"level2": {
"type": "level2Type",
"entry": [
"level2entry",
"level2entry"
]
}
}
}
I have the following Dart code:
Object jsonObject = JSON.parse("""{
"level1": {
"name": "level1name",
"level2": {
"type": "level2Type",
"entry": [
"level2entry",
"level2entry"
]
}
}
}
""");
print("my test 1 == ${jsonObject}");
print("my test 2 == ${jsonObject['level1']}");
print("my test 3 == ${jsonObject['level1']['name']}");
which produce the (desired) output:
my test 1 == {level1: {name: level1name, level2: {type: level2Type, entry: [level2entry, level2entry]}}}
my test 2 == {name: level1name, level2: {type: level2Type, entry: [level2entry, level2entry]}}
my test 3 == level1name
But when I try:
print("my test 1 == ${jsonObject.level1}");
I get the following:
Exception: NoSuchMethodException : method not found: 'get:level1'
Receiver: {level1: {name: level1name, level2: {type: level2Type, entry: [level2entry, level2entry]}}}
Arguments: []
Stack Trace: 0. Function: 'Object.noSuchMethod' url: 'bootstrap' line:717 col:3
Ideally, I want an object that I can access using the dot notation and without the compiler giving warning about Object not having property. I tried the following:
class MyJSONObject extends Object{
Level1 _level1;
Level1 get level1() => _level1;
set level1(Level1 s) => _level1 = s;
}
class Level1 {
String _name;
String get name() => _name;
set name(String s) => _name = s;
}
...
MyJSONObject jsonObject = JSON.parse("""{
"level1": {
"name": "level1name",
"level2": {
"type": "level2Type",
"entry": [
"level2entry",
"level2entry"
]
}
}
}
""");
...
print("my test 1 == ${jsonObject.level1.name}");
but instead of giving me 'level1name' as hoped, I get:
Exception: type 'LinkedHashMapImplementation<String, Dynamic>' is not a subtype of type 'MyJSONObject' of 'jsonObject'.
What am I doing wrong here? Is there any way to do what I'm trying? Thanks.
At the moment, JSON.parse only returns Lists (array), Maps, String, num, bool, and null
(api ref).
I suspect that until reflection makes it way into the language, it won't be able to re-construct objects based upon the keys found in json.
You could, however, create a constructor in your MyJsonObject which took a string, called JSON.parse internally, and assigned the various values.
Something like this works in the dart editor:
#import("dart:json");
class Level2 {
var type;
var entry;
}
class Level1 {
var name;
var level2;
}
class MyJSONObject {
Level1 level1;
MyJSONObject(jsonString) {
Map map = JSON.parse(jsonString);
this.level1 = new Level1();
level1.name = map['level1']['name'];
level1.level2 = new Level2();
level1.level2.type = map['level1']['level2']['type'];
//etc...
}
}
main() {
var obj = new MyJSONObject(json);
print(obj.level1.level2.type);
}
A non trivial version would needs some loops and possible recursion if you had deeper nested levels.
Update: I've hacked together a non-trivial version (inspired by the post below), it's up on github (also taking Seth's comments re the constructor):
Chris is completely right. I will only add that the JSON parser could be modified to return a little richer object (something like JsonMap instead of pure Map) that could allow jsonObj.property by implementing noSuchMethod. That would obviously perform worse than jsonObj['property'].