Strange behavior of JSON.parse() reviver function - javascript

I am using second parameter of JSON.parse() to modify the result, but I don't quite clear about the order of the function parameter and also how it work
I have read the document about the using of the reviver function (e.g https://www.ecma-international.org/ecma-262/6.0/#sec-json.parse and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse).
From what I understand, this function would work with object, and first parameter is key or property name, second function is value or property value. What I don't understand is the return value of the function.
This is what it is done in example
var obj1 = '{"a":1, "b":42}';
let text = JSON.parse(obj1, (key, value) => {
if (typeof value === 'number'){
return value * 2
}
else{
return value
}
}
)
console.log(text) // {"a": 2, "b": 84}
This work well. But when I try to modify the code since I know all value is number already
var obj1 = '{"a":1, "b":42}';
let text = JSON.parse(obj1, (key, value) =>{
return value * 2
})
console.log(text) // NaN
It is strange to me why when I delete the return value it doesn't work. I mean, with the function none of the value return undefined when I value*2 it. I then try another test
var obj1 = '{"a":1, "b":42}';
let text = JSON.parse(obj1, (key, value) => {
if (typeof value === 'number'){
console.log('This is in if',key, value)
return value * 2
}
else{
console.log('This is in else', key, value)
return value
}
}
)
console.log(text)
Another strange thing happen when the code in else statement run even when it suppose not to run because the condition is incorrect. And when it run it even print out the obj1 object, which I didn't include in the statement.

Because it will also iterate thorough the object which is {"a":1, "b":42}.It will start from most nested level and then will go the original value itself which is {"a":1, "b":42}.
According to MDN
If a reviver is specified, the value computed by parsing is transformed before being returned. Specifically, the computed value and all its properties (beginning with the most nested properties and proceeding to the original value itself) are individually run through the reviver
By the way you can shorten your function.
var obj1 = '{"a":1, "b":42}';
let text = JSON.parse(obj1, (_, value) => value * 2 || value)
console.log(text)

Related

How can I deserialize a nested React Element which is an elemen of a nested object inside an array?

Hey guys I have a tough one here
I need to store and retrieve an object with nested elements and a function which returns serialized in the clients localStorage. As for the storing and retrieving, it works. But I am having an issue with functions which have been stringyfied, but won't go back to be actual functions anymore.
Here is an example of an array element:
As far as I am aware, this is called serialized in the JSON-Jargon. And the goal would be to unserialize them again in order to make them function again. In order to be able to store functions in the JSON format, I had "replace and revive" the arrays which had to be stored.
let replacer = (key, value) => {
if (typeof value === "function") {
return value.toString();
}
return value;
I turn functions to string and return them to the JSON. I invoke this helper function like this:
localStorage.setItem(
"currentConfiguration",
JSON.stringify(
tableRef.current.dataManager.columns,
replacer,
2
)
Here is the code which is actually supposed to do: Undo what the replacer did and return actual functions instead of their strings.
let reviver = (key, value) => {
if (typeof value === "string" && value.indexOf("function ") === 0) {
let functionTemplate = `(${value})`;
return eval((functionTemplate));
}
return value;
};
Currently I am facing this screen whenever I try to render:
Would appriciate if anyone could lend a helping hand

How to refactor/fix algorithim from mutating a reference to using inline variable

The function takes an input path like a.b.c and should output a nested structure json like:
{
a: {
b: {
c: {}
}
}
}
The algorithm using iterative style is:
function stringToObj(path, obj) {
var parts = path.split(".");
var part;
while ((part = parts.shift())) {
if (typeof obj[part] != "object") obj[part] = {};
obj = obj[part]; // line 6
}
}
Current usage:
let result = {};
stringToObj("a.b.c", result);
console.log(result); // outputs the json
JsFiddle
The problem:
It relies mutating the obj parameter on line 6.
I would like to not rely upon passing the result object, and rather create one inside the function. Doing so results in different results. A desired example usage:
const result = stringToObj("a.b.c"); // result should be the json
Context:
The exercise is for learning purpose. Main objective is understanding why removing obj and rewriting the function as per follows doesn't work as expected:
function stringToObj(path) {
var obj = {};
var parts = path.split(".");
var part;
while ((part = parts.shift())) {
if (typeof obj[part] != "object") obj[part] = {};
obj = obj[part]; // line 6
}
return obj;
}
After splitting by .s, you can use reduceRight to start at the last property, c, while taking an initial value of an empty object. Inside the callback, use a computed property to return a new object containing the old object at the property being iterated over:
const stringToObj = str => str.split('.').reduceRight(
(lastObj, prop) => ({ [prop]: lastObj }), {}
);
console.log(stringToObj('a.b.c'));
If you're not familiar with it, reduceRight is like reduce, except that it iterates starting from the last element in the array and going backwards, instead of starting from the first element of the array and going forwards. On each iteration, the callback is called, where the first argument (here, lastObj) is the value returned from the last iteration, and the second argument is the current item of the array being iterated over.
You can also reverse the array of properties and use reduce instead of reduceRight, which might be easier to understand at a glance, but it's a bit less elegant:
const stringToObj = str => str.split('.').reverse().reduce(
(lastObj, prop) => ({ [prop]: lastObj }), {}
);
console.log(stringToObj('a.b.c'));
Also, don't mix var and let. If you're going to use ES2015 syntax - which you should - consider always using const, and only use let when you must reassign. Never use var, it has too many gotchas to be worth using in modern code.

How do I use JSON Stringify to replace all keys and values?

I'm working on a way that takes advantage of the replacer function argument in JSON.Stringify in JavaScript in order to change the word-case (toUpper /toLower case), the problem is my JSON is not straight key:value, some values are keys also and they have values themselves, so I need to go through all keys and values, check if the value is also a key and make sure I change the case (toUpper or toLower) for all keys and values.
I know that the replacer function in JSON.Stringify(object,ReplacerFunction) iterates through all keys and values and makes the modifications inside then return keys and values, but although I've been reading about this for a wWhile I can't apply it, and I am not sure if I should apply recursion inside the replacer function or how, any help is appreciated.
Code I had:
function _replacer(name,val){
if(typeof val != "object"){
return val.toString().toUpperCase()
}
if(typeof name != "object"){
return name.toString().toUpperCase()
}
console.log("key = "+name+" type: "+typeof name);
console.log("value ="+val+" type: "+typeof val);
}
Also:
function _replacer(name,val){
if(typeof val != "object" &&typeof val ==="string"){
return val=val.toUpperCase()
}
if(typeof name != "object" &&typeof name ==="string"){
name=name.toUpperCase()
}
return val;
}
Also , i eventually got to this stage :
var res = JSON.parse(JSON.stringify(j, function(key, value) {
return typeof value === "string" ? value.toUpperCase() : value
}));
but this code only capitalizes the very lower level values, not all the keys/values, the reason is because i can only return one value from the replacer function, which is in this case the value.
The replacer function in JSON.stringify, does not allow you to replace keys, as documented in MDN. It allows you to transform values, or to omit key/value pairs altogether (by returning undefined).
Your best bet is probably to transform the object before stringifying it. Underscore or Lodash would make this pretty easy, but you can do it natively without too much trouble like this:
const xform = obj => {
return Object.keys(obj).reduce((xformed, key) => {
let value = obj[key]
if (typeof value ==='string') value = value.toUpperCase()
else if (typeof value === 'object') value = xform(value)
xformed[key.toUpperCase()] = value
return xformed
}, {})
}
console.log(JSON.stringify(xform({a: 'b', c: 1, d: {e: 'f'}})))
// {"A":"B","C":1,"D":{"E":"F"}}
You could also use RegEx and replace after it is stringified if you are so inclined. The code is certainly shorter, but perhaps less readable:
const stringified = JSON.stringify({a: 'b', c: 1, d: {e: 'f'}})
console.log(stringified.replace(/".+?"/g, s => s.toUpperCase()))
// {"A":"B","C":1,"D":{"E":"F"}}

Javascript reserved word and object

I'm making a dictionary of words, so there are 1,000,000+ words.
The problem comes when I need to store the word constructor. I know this is a reserved word in javascript, but I need to add it to the dictionary.
var dictionary = {}
console.log(dictionary ['word_1'])
//undefined, this is good
console.log(dictionary ['word_2'])
//undefined, this is good
console.log(dictionary ['constructor'])
//[Function: Object]
// this cause initialization code to break
How can I fix this? I could muck with the it like key=key+"_" but that seems bad. Is there anything else I can do?
Instead of using a JS object, you could use the built-in Map type which uses strings/symbols as keys and does not conflict with any existing properties.
Replace
var dictionary = {} with var dictionary = new Map()
Override the constructor key as undefined
According to the MDN Object.prototype page, the only thing that isn't hidden by the __fieldname__ schema is the "constructor field". Thus, you could just initialize your objects via { 'constructor': undefined }.
However, you would have to make sure that in your for .. in statements would filter out all keys with undefined as their value, as it would pick up constructor as a "valid" key (even though it wouldn't before you specifically set it to undefined). I.E.
for(var key in obj) if(obj[key] !== undefined) { /* do things */ }
Check for types when getting/setting
Otherwise, you could just check the type when you 'fetch' or 'store' it. I.E.
function get(obj, key) {
if(typeof obj[key] !== 'function') // optionally, `&& typeof obj[key] !== 'object')`
return obj[key];
else
return undefined;
}
I think you should store all words and translation of them in an array. When you need to translate a word, you can use find method of Array.
For example:
var dict = [
{ word: "abc", translated: "xyz" },
...
];
Then:
var searching_word = "abc";
var translation = dict.find(function (item) {
return item.word == searching_word;
});
console.log(translation.translated);
// --> xyz
To achieve expected result , use below option of using index to get value of any key value
var dictionary = {};
var dictionary1 = {
constructor: "test"
};
//simple function to get key value using index
function getVal(obj, val) {
var keys = Object.keys(obj);
var index = keys.indexOf(val);//get index of key, in our case -contructor
return obj[keys[index]]; // return value using indec of that key
}
console.log(getVal(dictionary, "constructor"));//undefined as expected
console.log(getVal(dictionary1, "constructor"));//test
console.log(dictionary["word_1"]);
//undefined, this is good
console.log(dictionary["word_2"]);
//undefined, this is good
codepen - https://codepen.io/nagasai/pen/LOEGxM
For testing , I gave one object with key-constructor and other object without constructor.
Basically I am getting the index of key first and getting value using index

How is this helper $.each() function assigning values?

Will somebody please explain how part of this function operates?
var result = {}
$.each(this.serializeArray(), function(i, v) {
result[v.name] = v.value;
});
From the above code, I do not understand the following:
result[v.name] = v.value;
I don't understand how this is giving me the results I am getting, which is an object with name value pairs. How is this working?
// result is defined as empty object
var result = {}
// your each binding most likely is inside a $('form').submit(function(){ in here });
// so this.searializeArray() converts ( whatever "this" is in your scope ) an JavaScript array of objects you iterate over where function(i <- is the key, v <- is the object)
$.each(this.serializeArray(), function(i, v) {
// so for every object in your array you take the value and assign a new object in your initial empty result object with object.name as key and object.value as value
result[v.name] = v.value;
});
// so result may look like this after that
result == {
"fooname":"foovalue",
"barname":1337
}

Categories

Resources