Variable in JSON Path - JavaScript - javascript

I already searched for similar issues but I didn't find anything that could help me yet.
I'm trying to reach a picture path (using JSON format) depending on the material type of the picked element. Actually, my code is built like this:
if (globalData.Material.Mat_type == "OSCILLOSCOPE") {
var picture = (globalData.Material.Oscilloscope.picture);
}
if (globalData.Material.Mat_type == "ALIMENTATION") {
var picture = (globalData.Material.Alim.picture);
}
But not optimized at all, so Im trying to make it this way :
var mat_type = (globalData.Material.Mat_type);
var picture = (globalData.Material[mat_type].picture);
But it doesn't work... Got some exception:
TypeError : globalData.Material[mat_type] is undefined.
I already tried a lot of things, have you got any idea? Thanks!

I outlined the issue with character case in the comment under the question, so presumably adjusting value of globalData.Material.Mat_type could do the trick:
var mat_type =
globalData.Material.Mat_type.charAt(0).toUpperCase() +
globalData.Material.Mat_type.substr(1).toLowerCase();
I can also see that this general rule may not be applicable in all cases. If it's not a typo, it won't work for the second case where Mat_type == "ALIMENTATION", because then you try to access Alim property of Material instead of Alimentation. In this case you could access property by prefix:
function pictureOf(material) {
if (!material || !String(material.Mat_type)) {
return null;
}
let mat_type = String(material.Mat_type).toUpperCase();
for (var propertyName in material) {
if (mat_type.startsWith(propertyName.toUpperCase())) {
return material[propertyName].picture || null;
}
}
return null;
}
console.log(pictureOf({
Mat_type: "OSCILLOSCOPE",
Oscilloscope: {
picture: "picture of oscilloscope"
}
}));
console.log(pictureOf({
Mat_type: "ALIMENTATION",
Alim: {
picture: "picture of alimentation"
}
}));
But this kind of approach can be error prone, if multiple properties share the same prefix. There's also a hidden issue with case-insensitive prefix matching in case you use some special unicode characters in property names. Lastly this method is not efficient, because it has to iterate over all properties of the object (worst case scenario). It can be replaced with much safer property mapping:
const matTypeMapping = {
"ALIMENTATION": "Alim"
};
function pictureOf(material) {
if (!material || !String(material.Mat_type)) {
return null;
}
let matType = String(material.Mat_type);
// find property mapping or apply general rule, if mapping not defined
let propertyName = matTypeMapping[matType] ||
matType.charAt(0).toUpperCase() + matType.substr(1).toLowerCase();
return material[propertyName].picture || null;
}
console.log(pictureOf({
Mat_type: "OSCILLOSCOPE",
Oscilloscope: {
picture: "picture of oscilloscope"
}
}));
console.log(pictureOf({
Mat_type: "ALIMENTATION",
Alim: {
picture: "picture of alimentation"
}
}));
NB: To avoid headaches, maybe you should prefer strict equality operator over loose equality operator.

Problem Solved
Peter Wolf was right ! It was a case-sensitive issue
I actually don't know how to promote his comment, sorry for this..
Anyway, thank you guys !

var mat_type = (globalData.Material.Mat_type);
if(mat_type!==undefined)
var picture = (globalData.Material[mat_type].picture)
Just do an existential check before accessing the value, for keys that may not be present.

Related

Comparing 2 Json Object using javascript or underscore

PS: I have already searched the forums and have seen the relevant posts for this wherein the same post exists but I am not able to resolve my issue with those solutions.
I have 2 json objects
var json1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222", addrs:"def", tab:"tab2"}];
var json2 = [{id:"tab1"},{id:"new"}];
I want to compare both these and check if the id element in json2 is present in json1 by comparing to its tab key. If not then set some boolean to false. ie by comparing id:"tab1" in json2 to tab:"tab1 in json1 .
I tried using below solutions as suggested by various posts:
var o1 = json1;
var o2 = json2;
var set= false;
for (var p in o1) {
if (o1.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
for (var p in o2) {
if (o2.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
Also tried with underscore as:
_.each(json1, function(one) {
_.each(json2, function(two) {
if (one.tab!== two.id) {
set= true;
}
});
});
Both of them fail for some test case or other.
Can anyone tell any other better method or outline the issues above.
Don't call them JSON because they are JavaScript arrays. Read What is JSON.
To solve the problem, you may loop over second array and then in the iteration check if none of the objects in the first array matched the criteria. If so, set the result to true.
const obj1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222",addrs:"def", tab:"tab2"}];
const obj2 = [{id:"tab1"},{id:"new"}];
let result = false;
for (let {id} of obj2) {
if (!obj1.some(i => i.tab === id)) {
result = true;
break;
}
}
console.log(result);
Unfortunately, searching the forums and reading the relevant posts is not going to replace THINKING. Step away from your computer, and write down, on a piece of paper, exactly what the problem is and how you plan to solve it. For example:
Calculate for each object in an array whether some object in another array has a tab property whose value is the same as the first object's id property.
There are many ways to do this. The first way involves using array functions like map (corresponding to the "calculate for each" in the question, and some (corresponding to the "some" in the question). To make it easier, and try to avoid confusing ourselves, we'll do it step by step.
function calculateMatch(obj2) {
return obj2.map(doesSomeElementInObj1Match);
}
That's it. Your program is finished. You don't even need to test it, because it's obviously right.
But wait. How are you supposed to know about these array functions like map and some? By reading the documentation. No one help you with that. You have to do it yourself. You have to do it in advance as part of your learning process. You can't do it at the moment you need it, because you won't know what you don't know!
If it's easier for you to understand, and you're just getting started with functions, you may want to write this as
obj2.map(obj1Element => doesSomeElementInObj1Match(obj1Element))
or, if you're still not up to speed on arrow functions, then
obj2.map(function(obj1Element) { return doesSomeElementInObj1Match(obj1Element); })
The only thing left to do is to write doesSomeElementInObj2Match. For testing purposes, we can make one that always returns true:
function doesSomeElementInObj2Match() { return true; }
But eventually we will have to write it. Remember the part of our English description of the problem that's relevant here:
some object in another array has a tab property whose value is the same as the first object's id property.
When working with JS arrays, for "some" we have the some function. So, following the same top-down approach, we are going to write (assuming we know what the ID is):
In the same way as above, we can write this as
function doesSomeElementInObj2Match(id) {
obj2.some(obj2Element => tabFieldMatches(obj2Element, id))
}
or
obj2.some(function(obj2Element) { return tabFieldMatches(obj2Element, id); })
Here, tabFieldMatches is nothing more than checking to make sure obj2Element.tab and id are identical.
We're almost done! but we still have to write hasMatchingTabField. That's quite easy, it turns out:
function hasMatchingTabField(e2, id) { return e2.tab === id; }
In the following, to save space, we will write e1 for obj1Element and e2 for obj2Element, and stick with the arrow functions. This completes our first solution. We have
const tabFieldMatches = (tab, id) { return tab === id; }
const hasMatchingTabField = (obj, id) => obj.some(e => tabFieldMatches(e.tab, id);
const findMatches = obj => obj.some(e => hasMatchingTabField(e1, obj.id));
And we call this using findMatches(obj1).
Old-fashioned array
But perhaps all these maps and somes are a little too much for you at this point. What ever happened to good old-fashioned for-loops? Yes, we can write things this way, and some people might prefer that alternative.
top: for (e1 of obj1) {
for (e2 of (obj2) {
if (e1.id === e2.tab) {
console.log("found match");
break top;
}
}
console.log("didn't find match);
}
But some people are sure to complain about the non-standard use of break here. Or, we might want to end up with an array of boolean parallel to the input array. In that case, we have to be careful about remembering what matched, at what level.
const matched = [];
for (e1 of obj1) {
let match = false;
for (e2 of obj2) {
if (e1.id === e2.tab) match = true;
}
matched.push(match);
}
We can clean this up and optimize it bit, but that's the basic idea. Notice that we have to reset match each time through the loop over the first object.

Changing the JSON key and keeping its index same

I want to change the key of JSON attribute and keep/persist its position/Index.
E.g.
{"Test1" : {
mytest1:34,
mytest2:56,
mytest6:58,
mytest5:89,
}
}
Now I want to change the key mytest6 to mytest4 and keep its position as it is.
Note: In my case I can't use Array.
Thanks.
jsonObj = {"Test1" : {
mytest1:34,
mytest2:56,
mytest6:58,
mytest5:89,
}
};
var old_key = "mytest6";
var new_key = "mytest4";
if (old_key !== new_key) {
Object.defineProperty(jsonObj.Test1, new_key,
Object.getOwnPropertyDescriptor(jsonObj.Test1, old_key));
delete jsonObj.Test1[old_key];
}
console.log(jsonObj);
This method ensures that the renamed property behaves identically to the original one.
Also, it seems to me that the possibility to wrap this into a function/method and put it into Object.prototype is irrelevant regarding your question.
Fiddle

How can I compare a string to an object key and get that key's value?

I want to do something relatively simple, I think anyways.
I need to compare the pathname of page with an object's kv pairs. For example:
if("pathname" === "key"){return value;}
That's pretty much it. I'm not sure how to do it in either regular Javascript or jQuery. Either are acceptable.
You can see my fiddle here: http://jsfiddle.net/lz430/2rhds1x3/
JavaScript:
var pageID = "/electrical-electronic-tape/c/864";
var pageList = [{
"/electrical-electronic-tape/c/864": "ElectronicTape",
"/industrial-tape/c/889": "IndustrialTape",
"/sandblasting-tape/c/900": "SandblastingTape",
"/Foam-Tape/c/875": "FoamTape",
"/double-coated-d-c-dhesive-tape/c/872": "DCTape",
"/Adhesive-Transfer-Tape/c/919": "ATTape",
"/Reflective-Tape/c/884": "ReflectiveTape",
"/custom-moulding": "CustomMoulding",
"/request-a-quote": "RequestQuote"
}];
var label = pageID in pageList;
$('.el').html(label);
First, your "pageList" should just be a plain object, not an object in an array:
var pageList = {
"/electrical-electronic-tape/c/864": "ElectronicTape",
"/industrial-tape/c/889": "IndustrialTape",
"/sandblasting-tape/c/900": "SandblastingTape",
"/Foam-Tape/c/875": "FoamTape",
"/double-coated-d-c-dhesive-tape/c/872": "DCTape",
"/Adhesive-Transfer-Tape/c/919": "ATTape",
"/Reflective-Tape/c/884": "ReflectiveTape",
"/custom-moulding": "CustomMoulding",
"/request-a-quote": "RequestQuote"
};
Then you can set "label" to the value from the mapping:
var label = pageList[pageID] || "(not found)";
That last bit of the statement above will set the label to "(not found)" if the lookup fails, which may or may not be applicable to your situation.
It depends kinda on the logic you want to implement. If you want to say "if object has the key, then do X, and if not, then do Y", then you handle that differently than "set label to the object's key's value if the key is there, or else set it to undefined or something else".
For the first case you do:
if (pageList.hasOwnProperty(pageID) ) {
label = pageList[pageID];
}
else {
// do whatever, maybe some error?
}
For the second case, you can just say
var label = pageList[pageID] || 'notFound';
As indicated by #Pointy, either get rid of the array or subsiture pageList[0] for pageList and pageList[0][pageID] for pageList[pageID] above, if you need to keep the array.

jquery how to collect all link value from a object?

i have a object, just i need to collect and store the object which contains the lable as link in to a new array.. can any one give me the best way to do this?
myobeject:
var xploreMaps = {
radious:55,
stroke:5,strokeColor:'#fff',
opacity:0.8,fontSize:13,line:10,
cRtext:{
length:4,
lineColor:'#7d2c2c',
prop:{
0:{link:'motionGraphics.html',color:'#595959',text:'Motion Graphics'},
1:{link:'video.html',color:'#306465',text:'Video'},
2:{link:'photography.html',color:'#7e6931',text:'Photography'},
3:{link:'copyRight.html',color:'#4c4966',text:'Copywriting'}
}
},
cBtext:{
length:3,
lineColor:'#4c839d',
prop:{
0:{link:'imagination.html',color:'#595959',text:'Imagination'},
1:{link:'innovation.html',color:'#306465',text:'Innovation'},
2:{link:'ideation.html',color:'#7e6931',text:'Ideation'}
}
},
cGtext:{
length:5,
lineColor:'#579549',
prop:{
0:{link:'catalogs .html',color:'#7a5967',text:'Catalogs',
subLink:{0:{link:'SEO_SMM.html',color:'#4e4b69',text:'SEO/SMM',align:'top'},1:{link:'site_analytics.html',color:'#545454',text:'Site analytics',align:'btm'}}},
1:{link:'socialmedia.html',color:'#1e9ead',text:'Innovation'},
2:{link:'loyalty .html',color:'#8fad34',text:'Ideation'},
3:{link:'promotions .html',color:'#563b64',text:'Promotions'},
4:{link:'implementations.html',color:'#2c6566',text:'Implementations',
subLink:{0:{link:'integrating.html',color:'#4c4a66',text:'Integrating',align:'top'},1:{link:'payment.html',color:'#948048',text:'Payment',align:'btm'}}}
}
}
}
My function which i try:
var links = []//just i need all the objects which contains the link.
var objFinder = function (obj){
$.each(obj,function(key,val){
if(key == 'link' && typeof val == 'string'){
links.push(val)
}else{
objFinder(val);//throws errors;
}
})
}
objFinder(xploreMaps);
}
I think the main issue is that your objects have a property length. That is messing up the processing. See the fiddle I created here:
http://jsfiddle.net/8Zfdj/
I just commented out the length property and it seems to work properly. I also did some minor cleanup such as adding missing semi-colons but that wasn't the main issue.
You can see the jQuery bug (invalid) here:
http://bugs.jquery.com/ticket/7260

javascript "ors" - can they be combined into an array?

wondering if I can make my javascript more efficient.
I have a var JSP = "the jsp's name"
And I have statements in a javascript validation file:
if(( JSP == "test.html" ) || ( JSP == "test1.html") || ( JSP == "test2.html" )) then blah blah blah.
Is there a more efficient way to do this?
If you know that JSP contains a string, it's slightly more efficient to use === rather than ==. Also note that you don't need all those parens:
if (JSP === "test.html" || JSP === "test1.html" || JSP === "test2.html") {
// do something
}
You could also use a regular expression:
if (/^test[12]?\.html$/.test(JSP)) {
// do something
}
...but it depends what you mean by "efficient." The series of === will be very efficient at runtime.
Separately, you could use a switch:
switch (JSP) {
case "test.html":
case "test1.html":
case "test2.html":
// Do it
break;
}
...but I wouldn't call it more efficient.
I definitely would not put the options in an array, because searching through the array will not be efficient. But you can use a map:
var pages = {
"test.html": true,
"test1.html": true,
"test2.html": true
};
...and then this test:
if (pages[JSP] === true) {
// do something
}
...which results in a fairly efficient property lookup. That's only reasonable if you create the object once and reuse it.
(You might have people say "Or just use if (pages[JSP]) { ... }. But that fails if JSP happens to contain "toString" or "valueOf" or any of several other inherited properties blank objects get from Object.prototype. It's fine if you're certain it won't have any of those values, though.)
You could create an object with those keys:
var theThings = { "test.html": true, "test1.html": true, "test2.html": true };
if (theThings[JSP]) { /* whatever */ }
If there are only three or four, it might not be worth it, but if there are dozens it'd definitely be faster, especially if the test gets made several times.
edit — wow I'm crying a little inside here, guys. Property name lookups are going to be way faster than linear searches through an array.
var arr = ['test.html', 'test1.html', 'test2.html'];
if (arr.indexOf(JSP)) != -1) {
alert("found it!");
}
relevant docs here.
if( JSP in {"test.html":0, "test2.html":0, "test3.html":0} ) {
...
}
It doesn't get any closer to SQL's IN( 1, 2, 3) than this in javascript :-)
if (["test.html", "test1.html", "test2.html"].indexOf(JSP) > -1)
For browsers that don't support indexOf on arrays, MDC suggests short piece of code that adds missing functionality.
Probably not more efficient, but you got cleaner ways to do it. You could for instance use a switch-case like this:
switch(JSP) {
case 'test.html':
case 'test1.html':
case 'test2.html':
blablabla; break;
}
Or you could create an array out of the urls and see if your string is in the array like this
var arr = [
'test.html', 'test1.html',
'test2.html'
];
if(arr.indexOf(JSP) != -1) { blablabla; }
The last one will not work in all browsers.
A way of doing it in jQuery is to use the inArray method, e.g.:
if ($.inArray(JSP, ["test.html", "test1.html", "test2.html"]) > -1) {
// your code
}
The inArray method works in a similar manner to String.indexOf so -1 is returned if no match.
Use a regular expression?
if (/^test\d?\.html$/.test(JSP)) { ... }
I can't promise that will be more efficient though, just tidier code-wise.
Or if you're already using jQuery, you could use jQuery.inArray():
var names = ['test.html', 'test2.html', 'test3.html'];
if ($.inArray(JSP, names)) { ... }
Or with underscore.js
var names = ['test.html', 'test2.html', 'test3.html'];
if (_.indexOf(names, JSP) !== -1) { ... }

Categories

Resources