Assume I have the following object:
var jsonObj = {
"response":{
"result":{
"status":{
"name": "Eric"
}
}
}
}
And now i'd like to dynamically access a nested property:
jsonKey = "response.result.status.name";
console.log("the status is: " + jsonObj.jsonKey); //I cannot call jsonObj.jsonKey here
Is there any way to achieve this?
You cannot access a deeply nested property as simple as you expect. Instead you need to use the obj[propertyNameAsString] syntax to dive deeper into the response one by one.
This would be one way of getting there:
let response = {
"response": {
"method": "GetStatus",
"module": "Module",
"data": null,
"result": {
"status": {
"name": "Eric"
},
"id": 1
},
"result_code": {
"error_code": 0
}
}
}
let keyString = "response.result.status.name"
let keyArray = keyString.split('.'); // [ "response", "result", "status", "name" ]
var result = response;
for (key of keyArray) {
result = result[key]
}
console.log(result)
Please be aware that this is not failsafe against cases where one of those strings in keyArray does not exist as a property on the preceding object.
You can do like this something['bar']
Where bar is your variable that has been converted to string, in our case:
jsonObj[`${jsonKey}`]
Related
I have an json data and I wanna create a new object of it according a specific property of the json data; The value of this dynamic key should be an array and I need to update this array if a similar key founded in json data; But I got this error and I don't know what is my bug index.js:46 Uncaught TypeError: object[fullDate] is not iterable
function createGridObject(data) {
let object = {};
for (const item of data) {
const date = new Date(item.orderInfo.demandTime);
const fullDate = `${date.getFullYear()}-${date.getMonth()}-${date.getDay()}`;
console.log({fullDate});
object = {
...object,
[fullDate]: [...object[fullDate], ...item],
};
}
console.log({object});
}
[
{
"id": "2c68be90-6186-44ef-a963-4b5f36d9afe4",
"orderInfo": {
"partNumber": "YDN2ZEP279P1",
"type": "FULL",
"origin": "SU-H40V1",
"destination": "41A01L-T1",
"demandTime": "2021-04-13T21:07:01.587440Z",
"externalOrderId": "181788528",
"containerType": "VW0001",
"received": "2021-04-13T21:02:02.567298Z",
"trailerPosition": null
},
},
{
"id": "1460b736-d6f5-4187-8acc-74f748c8197a",
"orderInfo": {
"partNumber": "",
"type": "EMPTY",
"origin": "SU-H40V1",
"destination": "42A05L-T1",
"demandTime": "2021-04-13T22:27:21.099507Z",
"externalOrderId": "891755586",
"containerType": "VW0001",
"received": "2021-04-13T22:22:24.268943Z",
"trailerPosition": null
}
},
]
If object[fullDate] doesn't exist, [...object[fullDate], ___] is trying to use iterable spread on undefined. You can't do that. (People sometimes get confused, because you can use object property spread on undefined. But not iterable spread.)
Instead:
object = {
...object,
[fullDate]: [...object[fullDate] ?? [], ...item],
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^
};
That way, if it's undefined, you'll spread [] instead.
Or use a conditional:
object = {
...object,
[fullDate]: object[fullDate] ? [...object[fullDate], ...item] : [...item],
};
This question might already been asked but I'm having some trouble understanding it, I'd like to update my javascript object with new objects.
Object 1 :
var cjson = {};
var t = {
"infos": "apple",
"fields": {
"color":"red",
}
}
cjson['g320fld1'] = t;
Object 2 :
var data {
"fruits": {},
"vegetables": {}
}
Output : I want to push object 1 to object 2 under fruits key. so the ouput look :
{
"fruits": {
"g320fld1": {
"infos": "apple",
"fields": {
"color":"red",
}
},
"vegetables": {}
}
What I tried :
push()
data['fruits'].push(cjson);
Error : ...push() is not a function. (I know push() works on array only so it won't work here.)
update()
data['fruits'].update(cjson);
Error : ...update() is not a function. (this one gives the same error but since it's another dictionary shouldn't it work as expected ?)
How can I solve this problem ?
UPDATE :
Sorry I didn't precise, I don't want to erase older data in fruits.
You can just assign t with the key directly to the data object. As long as your keys(g320fld1 for example) are distinct nothing will be overwritten. I think this makes more sense for what you are trying to do.
var t = {
"infos": "apple",
"fields": {
"color":"red",
}
}
var data = {
"fruits": {},
"vegetables": {}
}
data.fruits['g320fld1'] = t;
console.log(data);
EDIT
You can use Object.assign(srcObject,newProperties) to append new properties,values to an existing object.
var cjson = {};
var t = {
"infos": "apple",
"fields": {
"color":"red"
}
}
cjson['g320fld1'] = t;
var data ={
"fruits": {
otherProperty:"bar"
},
"vegetables": {}
}
Object.assign(data.fruits,cjson);
console.log(data)
I am using the following to check for duplicate values in an array before I add a value:
vm.onGridItemChanged = function (resource) {
if (vm.modifiedItems.indexOf(resource) === -1) {
vm.modifiedItems.push(resource);
}
};
This works great and gives me behaviour I want. However I now have need to add EnglishValue also, so I have changed to:
vm.onGridItemChanged = function (resource, englishText) {
var modifiedItem = {Resource: resource, EnglishValue: englishText};
if (vm.modifiedItems.indexOf(modifiedItem) === -1) {
vm.modifiedItems.push(modifiedItem);
}
};
However this breaks the functionality and I am now getting duplicate values? Why would this be the case?
Edit - my josn includes $$hashKey values that is still causing duplicate issues:
[
{
"Resource": {
"ResourceId": "Account_AccountVerified_Success_Title",
"LocaleId": "de",
"ResourceSet": "/WebResources",
"Value": "Erfolg55",
"Comment": "Success",
"Editing": false,
"$$hashKey": "object:3861"
},
"EnglishValue": "Success",
"$$hashKey": "object:40483"
},
{
"Resource": {
"ResourceId": "Account_AccountVerified_Success_Title",
"LocaleId": "de",
"ResourceSet": "/WebResources",
"Value": "Erfolg55",
"Comment": "Success",
"Editing": false,
"$$hashKey": "object:3861"
},
"EnglishValue": "Success",
"$$hashKey": "object:40488"
}
]
You have to convert the JSON object to string and check its index. Since, you have $$hashkey in your object, you first need to remove that by angular.toJson
vm.onGridItemChanged = function (resource, englishText) {
var modifiedItem = {Resource: resource, EnglishValue: englishText};
var modifiedItems = angular.toJson(vm.modifiedItems);
if (JSON.stringify(modifiedItems).indexOf(JSON.stringify(modifiedItem)) === -1) {
vm.modifiedItems.push(modifiedItem);
}
};
var a = [{'d':'dasdasd','e':'dasdasda'}, {'d':'dasdasd','e':'dada'}];
var b = {'d':'dasdasd','e':'dasdasda'};
var index = JSON.stringify(a).indexOf(JSON.stringify(b));
console.log(index);
See that we get the index of the JSON object that exist in the string. Likewise, index will be -1 if not present on the JSON array.
I have a response from a web service and want to replace some values in the response with my custom values.
One way is to write a tree traverser and then check for the value and replace with my custom value
so the response is some what like this:
[
{
"name": "n1",
"value": "v1",
"children": [
{
"name": "n2",
"value": "v2"
}
]
},
{
"name": "n3",
"value": "v3"
}
]
now my custom map is like this
const map = {
"v1": "v11",
"v2": "v22",
"v3": "v33"
};
All I want is
[
{
"name": "n1",
"value": "v11",
"children": [
{
"name": "n2",
"value": "v22"
}
]
},
{
"name": "n3",
"value": "v33"
}
]
I was thinking if I could stringify my response and then replace values using a custom build regex from my map of values.
Will it be faster as compared to tree traverser?
If yes, how should I do that?
somewhat like this
originalString.replace(regexp, function (replacement))
The tree traversal is faster
Note that some things could be done more efficiently in the regex implementation but I still think there are some more bottlenecks to explain.
Why the regex is slow:
There are probably many more reasons why the regex is slower but I'll explain at least one significant reason:
When you're using regex to find and replace, you're using creating new strings every time and performing your matches every time. Regex expressions can be very expensive and my implementation isn't particularly cheap.
Why is the tree traversal faster:
In the tree traversal, I'm mutating the object directly. This doesn't require creating new string objects or any new objects at all. We're also not performing a full search on the whole string every time as well.
RESULTS
run the performance test below. The test using console.time to record how long it takes. See the the tree traversal is much faster.
function usingRegex(obj, map) {
return JSON.parse(Object.keys(map).map(oldValue => ({
oldValue,
newValue: map[oldValue]
})).reduce((json, {
oldValue,
newValue
}) => {
return json.replace(
new RegExp(`"value":"(${oldValue})"`),
() => `"value":"${newValue}"`
);
}, JSON.stringify(obj)));
}
function usingTree(obj, map) {
function traverse(children) {
for (let item of children) {
if (item && item.value) {
// get a value from a JS object is O(1)!
item.value = map[item.value];
}
if (item && item.children) {
traverse(item.children)
}
}
}
traverse(obj);
return obj; // mutates
}
const obj = JSON.parse(`[
{
"name": "n1",
"value": "v1",
"children": [
{
"name": "n2",
"value": "v2"
}
]
},
{
"name": "n3",
"value": "v3"
}
]`);
const map = {
"v1": "v11",
"v2": "v22",
"v3": "v33"
};
// show that each function is working first
console.log('== TEST THE FUNCTIONS ==');
console.log('usingRegex', usingRegex(obj, map));
console.log('usingTree', usingTree(obj, map));
const iterations = 10000; // ten thousand
console.log('== DO 10000 ITERATIONS ==');
console.time('regex implementation');
for (let i = 0; i < iterations; i += 1) {
usingRegex(obj, map);
}
console.timeEnd('regex implementation');
console.time('tree implementation');
for (let i = 0; i < iterations; i += 1) {
usingTree(obj, map);
}
console.timeEnd('tree implementation');
Will it be faster as compared to tree traverser?
I don't know. I think it would depend on the size of the input, and the size of the replacement map. You could run some tests at JSPerf.com.
If yes, how should I do that?
It's fairly easy to do with a regex-based string replacement if the values you are replacing don't need any special escaping or whatever. Something like this:
const input = [
{
"name": "n1",
"value": "v1",
"children": [
{
"name": "n2",
"value": "v2"
}
]
},
{
"name": "n3",
"value": "v3"
}
];
const map = {
"v1": "v11",
"v2": "v22",
"v3": "v33"
};
// create a regex that matches any of the map keys, adding ':' and quotes
// to be sure to match whole property values and not property names
const regex = new RegExp(':\\s*"(' + Object.keys(map).join('|') + ')"', 'g');
// NOTE: if you've received this data as JSON then do the replacement
// *before* parsing it, don't parse it then restringify it then reparse it.
const json = JSON.stringify(input);
const result = JSON.parse(
json.replace(regex, function(m, key) { return ': "' + map[key] + '"'; })
);
console.log(result);
definitely traverser go faster as string replace means travels against each characters in the final string as opposed to iterator that can skips no necessarily item.
I have the following JSON:
{
"responseObject": {
"name": "ObjectName",
"fields": [
{
"fieldName": "refId",
"value": "2170gga35511"
},
{
"fieldName": "telNum",
"value": "4541885881"
}]}
}
I want to access "value" of the the array element with "fieldName": "telNum" without using index numbers, because I don't know everytime exactly at which place this telNum element will appear.
What I dream of is something like this:
jsonVarName.responseObject.fields['fieldname'='telNum'].value
Is this even possible in JavaScript?
You can do it like this
var k={
"responseObject": {
"name": "ObjectName",
"fields": [
{
"fieldName": "refId",
"value": "2170gga35511"
},
{
"fieldName": "telNum",
"value": "4541885881"
}]
}};
value1=k.responseObject.fields.find(
function(i)
{return (i.fieldName=="telNum")}).value;
console.log(value1);
There is JSONPath that lets you write queries just like XPATH does for XML.
$.store.book[*].author the authors of all books in the store
$..author all authors
$.store.* all things in store, which are some books and a red bicycle.
$.store..price the price of everything in the store.
$..book[2] the third book
$..book[(#.length-1)]
$..book[-1:] the last book in order.
$..book[0,1]
$..book[:2] the first two books
$..book[?(#.isbn)] filter all books with isbn number
$..book[?(#.price<10)] filter all books cheapier than 10
$..* All members of JSON structure.
You will have to loop through and find it.
var json = {
"responseObject": {
"name": "ObjectName",
"fields": [
{
"fieldName": "refId",
"value": "2170gga35511"
},
{
"fieldName": "telNum",
"value": "4541885881"
}]
};
function getValueForFieldName(fieldName){
for(var i=0;i<json.fields.length;i++){
if(json.fields[i].fieldName == fieldName){
return json.fields[i].value;
}
}
return false;
}
console.log(getValueForFieldName("telNum"));
It might be a better option to modify the array into object with fieldName as keys once to avoid using .find over and over again.
fields = Object.assign({}, ...fields.map(field => {
const newField = {};
newField[field.fieldName] = field.value;
return newField;
}
It's not possible.. Native JavaScript has nothing similar to XPATH like in xml to iterate through JSON. You have to loop or use Array.prototype.find() as stated in comments.
It's experimental and supported only Chrome 45+, Safari 7.1+, FF 25+. No IE.
Example can be found here
Clean and easy way to just loop through array.
var json = {
"responseObject": {
"name": "ObjectName",
"fields": [
{
"fieldName": "refId",
"value": "2170gga35511"
},
{
"fieldName": "telNum",
"value": "4541885881"
}]
}
$(json.responseObject.fields).each(function (i, field) {
if (field.fieldName === "telNum") {
return field.value // break each
}
})