Nicer way to get nested object attributes - javascript

Often in a response from a remote API call, I receive nested objects:
var response = {
data : {
users : [
{
name : 'Mr. White'
}
]
}
}
I want to check whether the first user's name is 'Mr. White', and would naturally want to write something like.
var existed = response.data.users[0].name === 'Mr. White'
However I cannot be sure if all the objects are present, so to avoid exceptions instead I end up writing:
var existed = response && response.data && response.data.users && response.data.users[0].name === 'Mr. White'
Is there a nicer way to do this? Another ugly option that comes to mind is:
var existed = false;
try {
var existed = response.data.users[0].name === 'Mr. White';
} catch(e) { }
In addition to vanilla javascript, I usually have underscore.js and jquery available too.
Edit:
Oops, noticed I asked a dupe of javascript test for existence of nested object key.
An interesting option based on those answers is:
var existed = (((response || {}).data || {}).users || [{}])[0].name === 'Mr. White';

You could hide this naughty try/catch block inside a function like this one :
function resolve(root, path){
try {
return (new Function(
'root', 'return root.' + path + ';'
))(root);
} catch (e) {}
}
var tree = { level1: [{ key: 'value' }] };
resolve(tree, 'level1[0].key'); // "value"
resolve(tree, 'level1[1].key'); // undefined
More on this : https://stackoverflow.com/a/18381564/1636522

I would use the try catch approach but wrap it in a function to hide the ugliness.

Instead of a try/catch, this should be done via checking whether each level in the object is defined or not.
go for
if(typeof(response)!="undefined"
&& typeof(response.data)!="undefined"
&& typeof(response.data.users)!="undefined"
&& typeof(response.data.users[0])!="undefined"
&& typeof(response.data.users[0].name)!="undefined"
) {
//executes only if response.data.users[0].name is existing
}

Here is a function which I used in one of my projects http://jsfiddle.net/JBBAJ/
var object = {
data: {
users: [
{
firstName: "White"
},
{
firstName: "Black"
}
]
}
}
var read = function(path, obj) {
var path = path.split(".");
var item = path.shift();
if(item.indexOf("]") == item.length-1) {
// array
item = item.split("[");
var arrayName = item.shift();
var arrayIndex = parseInt(item.shift().replace("]", ""));
var arr = obj[arrayName || ""];
if(arr && arr[arrayIndex]) {
return read(path.join("."), arr[arrayIndex]);
} else {
return null;
}
} else {
// object
if(obj[item]) {
if(path.length === 0) {
return obj[item];
} else {
return read(path.join("."), obj[item]);
}
} else {
return null;
}
}
}
console.log(read("data.users[0].firstName", object)); // White
console.log(read("data.users[1].firstName", object)); // Black
console.log(read("data.test.users[0]", object)); // null
The idea is to pass your path as a string along with your object. The idea was to prevent the throwing of an exception and receive just null as result of the path is wrong. The good thing is that the function works with every path and you don't need to write long if statements.

Related

React object property assignment only works the first time

For the following code, parameters are js objects whose structures are initialized as follows:
statePiece = {
field_name: { disabled: false, exampleValue: "arbitrary" },
field_name2: {
/* ... */
},
field_nameN: {
/* ... */
}
};
userField = "field_name_string";
sesarValues = {
format: "one2one",
selectedField: "latitude",
disabledSelf: true,
addField: 0
};
This function works correctly and returns the modified statePiece as returnTemp the first time a particular statePiece.field_name is modified
export let setUserField = (statePiece, userField, sesarValues) => {
console.log("set user field", userField, "set mappval", sesarValues);
var temp = { ...statePiece }; //(this.state.fields[each].mappedTo != null) ? (this.state.fields[userField].mappedTo) : [];
var XUnit = statePiece[userField];
if (typeof userField != "string") {
console.log("not string");
for (var each of userField) {
if (sesarValues) {
temp[each].mappedTo = sesarValues.selectedField;
temp[each].disabled = true;
} else {
temp[each].disabled = !temp[each].disabled;
delete temp[each].mappedTo;
}
}
} else {
//is string
console.log("is string");
console.log(XUnit);
if (sesarValues) {
if (XUnit.disabled === true) XUnit.disabled = false;
console.log("1");
console.log(XUnit);
XUnit.disabled = true;
console.log(XUnit);
XUnit.mappedTo = sesarValues.selectedField;
} else {
console.log("2");
temp[userField].disabled = !temp[userField].disabled;
delete temp[userField].mappedTo;
}
}
let returnTemp = { ...temp, [userField]: XUnit };
console.log("set UF debug ", returnTemp);
console.log(returnTemp["FACILITY_CODE"]);
return returnTemp;
};
But after that, changing the statePiece.userField.mappedTo value fails to alter the object property. Or at least alter it permanently. When I console log the returnTemp variable, I see the entry has lost its mappedTo entry(as should happen) without it being replaced with the new userField.
However, when I console.log(returnTemp[userField]) it shows the entry values with the expected mappedTo key: value pair.
Not sure what's going on here.
From the usage of userField, I can work out that it could be an Array or a String.
However you have done something curious with it in the following expression:
var XUnit = statePiece[userField];
Given userField is a String, the above expression is fine.
However, where it is an array, XUnit will be undefined.
Also doing the same where userField is an Array in the following line means that you're setting the userField.toString() as a key mapped to undefined.
let returnTemp = { ...temp, [userField]: XUnit };
I'd assign XUnit where the condition checks out that userField is a String and just return temp.
else {
//is string
var XUnit = statePiece[userField];
//...
}
return temp;

Call functions from sources directly in Chrome console?

For a website there is this function under sources with the code:
betSlipView.prototype.stakeOnKeyUp = function(_key) {
var model = ob.slip.getModel(),
defval = ob.cfg.default_bet_amount;
selector = toJqId(["#stake-", _key].join('')),
stake_box = $(selector),
spl = stake_box.val();
if(spl != defval) {
spl = ob.slip.cleanFormatedAmount(spl);
if(spl === '' || isNaN(spl)) {
spl = 0;
$(selector).val('');
}
model.setBetStake(_key, spl);
$(toJqId(['#ob-slip-estimate-', _key].join(''))).html(
model.getBet(_key, 'pretty_returns')
);
} else {
$(selector).val(defval);
model.setBetStake(_key, defval);
$(toJqId(['#ob-slip-estimate-', _key].join(''))).html(
model.getBet(_key, 'pretty_returns')
);
}
//Update bonus amount
try {
var offers = model.getBet(_key, 'offers');
}
catch(err) {
var offers = "";
}
if(offers !== "" && typeof offers['STLWIN'] !== "undefined") {
this._handleAccumulatorBonusElements(_key, offers['STLWIN']);
};
// potential returns for this bet
this.updateTotals();
};
I cannot figure out how to (if possible) call this function directly from the console. Firstly, when I try to write betSlipView in the console, it cannot be found. Consequently if I copy the code to the console to define the function, betSlipView is still not found and if I try to change the function name, there are some names in the function body that cannot be found either. I wish to call this function with certain arguments, is this possible?
The whole code can be found here https://obstatic1.danskespil.dk/static/compressed/js/ob/slip/crunched.pkg.js?ver=0305f181cb96b61490e0fd2adafa3a91

API Key with colon, parsing it gives TypeError: forEach isn't a function

I'm trying to use New York Times API in order to get the Top Stories in JSON but I keep on getting a:
Uncaught TypeError: top.forEach is not a function
I feel like there's something wrong with the API key since it has : colons in the url. I even tried to encode it with %3A but it still doesn't work.
This is the basic url:
http://api.nytimes.com/svc/topstories/v2/home.json?api-key={API-KEY}
My function that grabs the data from the url:
```
function topStories(topStoriesURL) {
$.getJSON(topStoriesURL, function(top) {
top.forEach(function(data) {
link = data.results.url;
cardTitle = data.results.title;
if(data.results.byline == "") { postedBy = data.results.source; }
else { postedBy = data.results.byline; }
imgSource = data.results.media[0].media-metadata[10].url;
createCardElements();
});
});
}
I console.log(url) and when I click it inside Chrome console, it ignored the part of the key that comes after the colon. I've been debugging, but I can't seem to figure out the error.
Here is a version of the code that works.
function topStories(topStoriesURL) {
$.getJSON(topStoriesURL, function(data) {
if (data.error) {
alert('error!'); // TODO: Add better error handling here
} else {
data.results.forEach(function(result) {
var link = result.url,
cardTitle = result.title,
postedBy = result.byline == "" ? result.source : result.byline,
hasMultimedia = (result.multimedia || []).length > 0,
imgSource = hasMultimedia ? result.multimedia[result.multimedia.length - 1].url : null;
createCardElement(link, cardTitle, postedBy, imgSource);
});
}
});
}
function createCardElement(link, title, postedBy, imgSource) {
// create a single card element here
console.log('Creating a card with arguments of ', arguments);
}
topStories('http://api.nytimes.com/svc/topstories/v2/home.json?api-key=sample-key');
You are most likely going to need to do a for ... in loop on the top object since it is an object. You can not do a forEach upon on object the syntax would probably look like this:
function topStories(topStoriesURL) {
$.getJSON(topStoriesURL, function(top) {
for (var datum in top) {
link = datum.results.url;
cardTitle = datum.results.title;
if(datum.results.byline == "") { postedBy = datum.results.source; }
else { postedBy = datum.results.byline; }
imgSource = datum.results.media[0].media-metadata[10].url;
createCardElements();
});
});
}
Heres the documentation on for...in loops

If statement not returning as supposed

I might seem really dumb but this piece of code is really frustating me.
if(fs.exist(parametters[0])){
fs.remove(parametters[0]);
return "removed";
}else{
return "doesn't exist"
}
The thing is, the fs.remove() is actually called but the function is returning "doesnt't exist", Am I missing something?
I'm not using nodejs, this is from one library i made, is asynchronously.
It's not modifying the parametters but it does change the condition, might be that?
Well I'm posting my fs object although I don't think this will change anything.
fs = {
load: function() {
if (localStorage[0] == undefined || localStorage[0] == "undefined" || localStorage[0] == "") {
localStorage[0] = JSON.stringify(fs.files);
} else {
fs.files = JSON.parse(localStorage[0]);
}
},
save: function() {
localStorage[0] = JSON.stringify(fs.files);
},
files: [],
newFile: function(name, content, overwrite) {
if (overwrite == undefined)
overwrite = true;
if (fs.exist(name) && overwrite) {
fs.find(name).content = content;
fs.save();
}
if (!(fs.exist(name))) {
fs.files.push({
name: name,
content: content
});
fs.save();
}
},
exist: function(fileName) {
for (var i = 0; i < fs.files.length; i++) {
if (fs.files[i].name == fileName)
return true;
}
return false;
},
find: function(fileName) {
for (var i = 0; i < fs.files.length; i++) {
if (fs.files[i].name == fileName)
return fs.files[i];
}
return false;
},
format: function() {
fs.files = [];
localStorage[0] = undefined;
},
write: function(name, content, overwrite) {
if (overwrite == undefined)
overwrite = true;
if (fs.exist(name) && overwrite) {
fs.find(name).content = content;
fs.save();
}
if (!(fs.exist(name))) {
fs.files.push({
name: name,
content: content
});
fs.save();
}
},
remove: function(file) {
var arrToreturn = [];
for (var i = 0; i < fs.files.length; i++) {
if (fs.files[i].name != file)
arrToreturn.push(fs.files[i]);
}
fs.files = arrToreturn;
fs.save();
return arrToreturn;
}
}
Resolved -
After a few days of inspecting the code I found the bug where the function was called twice, the amount of code was really huge so it took me a while.
You need to add a semi-colon to return "doesn't exist", it should read return "doesn't exist";
If this is an Object, still it works.
We can assume this to be an File object ARRAY, indexOf still works to find if the item exists.
Please have a look upon below example:
var fruits = ["Banana", "Orange", "Apple", "Mango"];
var a = fruits.indexOf("Apple");
Result is 2 in case Apple is found
Result is -1 in case Apple is not found
You can have some more options at this link: http://www.w3schools.com/jsref/jsref_indexof_array.asp
Thanks
Use This Code For Solving This Problam. thanks
var fs = require('fs-extra')
fs.remove('/tmp/myfile', function (err) {
if (err) return console.error(err)
console.log('success!')
})
fs.removeSync('/home/jprichardson') //I just deleted my entire HOME directory.
You can try javascript indexOf function to check if the value really exists, BEFORE REMOVE Operation.
Example below:
var str = "Hello world, welcome to the universe.";
var n = str.indexOf("welcome");
=> Gives 13 if found
if we search for "welcome1" -> will give -1

how to change attribute text of json in jquery?

I am trying to change the property name /attr name of my json object.I try like that but nothing will change.I need to make json object after seen the input json and convert it like outjson
function changeData(data){
var title;
for(var i = 0; i < data.length; i++){
if(data[i].hasOwnProperty("displayName")){
data[i]["label"] = data[i]["displayName"];
delete data[i]["displayName"];
}
if(data[i].hasOwnProperty("displayDetail")){
data[i]["title"] = data[i]["displayDetail"];
delete data[i]["displayDetail"];
}
if(data[i].hasOwnProperty("inputType")){
if(data[i]["inputType"]=="NUMBER"){
data[i]["type"]="number"
}else if(data[i]["inputType"]=="TEXT"){
data[i]["type"]="text"
}else if(data[i]["inputType"]=="SWTICH"){
data[i]["type"]="select"
}
delete data[i]["inputType"];
}
}
console.log(data);
}
Try this - it's possibe to remove the if selection for inputType by creating a tiny lookup table from original value to new value:
function changeData(data) {
var map = { NUMBER: "number", TEXT: "text", SWITCH: "select" };
// data is an object - use for .. in to enumerate
for (var key in data.input) {
var e = data.input[key]; // alias for efficient structure dereferencing
e.label = e.displayName;
e.title = e.displayDetail;
e.type = map[e.inputType];
delete e.displayName;
delete e.displayDetail;
delete e.inputType;
}
};
There's really no need for the hasOwnProperty test these days - only use it if you think there's any risk that someone unsafely added to Object.prototype. jQuery manages without it quite happily, other modern code should do to.
If the mapping of field names was any longer I'd consider using another mapping table with another loop to remove the hard coded copy/delete pairs.
i have a nice Recursive function for that:
usage:
// replace list
var replacedObj = replaceAttrName(sourceObject, {foo: 'foooo', bar: 'baaar'});
so in your case you can easily do:
var newObj = replaceAttrName(json, {displayDetail: 'title', displayName: 'label', inputType: 'type'});
demo: http://jsfiddle.net/h1u0kq67/15/
the function is that:
function replaceAttrName(sourceObj, replaceList, destObj) {
destObj = destObj || {};
for(var prop in sourceObj) {
if(sourceObj.hasOwnProperty(prop)) {
if(typeof sourceObj[prop] === 'object') {
if(replaceList[prop]) {
var strName = replaceList[prop];
destObj[strName] = {};
replaceAttrName(sourceObj[prop], replaceList, destObj[strName]);
} else if(!replaceList[prop]) {
destObj[prop] = {};
replaceAttrName(sourceObj[prop], replaceList, destObj[prop]);
}
} else if (typeof sourceObj[prop] != 'object') {
if(replaceList[prop]) {
var strName = replaceList[prop];
destObj[strName] = sourceObj[prop];
} else if(!replaceList[prop]) {
destObj[prop] = sourceObj[prop];
}
}
}
}
return destObj;
}
If I am getting you right, you just want substitutions:
displayDetail => title
displayName => label
inputType => type.
I came up with the follwoing:
function changeData(data){
return JSON.parse(JSON.stringify(data).replace(/displayDetail/g, "title").replace(/displayName/g, "label").replace(/inputType/g, "type"));
}
Here is the Fiddle to play with.
Edit: I forgot replacements for "NUMBER", "TEXT" and "SWITCH".
function changeData(data){
return JSON.parse(JSON.stringify(data).replace(/displayDetail/g, "title").replace(/displayName/g, "label").replace(/inputType/g, "type").replace(/TEXT/g, "text").replace(/NUMBER/g, "number").replace(/SWITCH/g, "switch"));
}

Categories

Resources