I'm using getJSON for my JSON data items, like this:
$.getJSON(projects, function (json) {
var firstThree = json.sort(function(a, b) { return a.Variable1 < b.Variable1 ? 1 : -1; })
.slice(0, 3);
var related = firstThree;
var i = '';
$.each(json, function(i) {
var sizeClass = JSON.stringify(related[i].pname);
console.log('sifzdfze'+sizeClass);
});
I checked on console, getting sizeClass item, it works. But but it also shows an error:
related[i] is undefined
What's the problem? Thanks!
As per your code you are accessing related as an array. So you are supposed to iterate over the items in the array. In your case related[i] is undefined because you are trying to access an item that is out of bounds of the particular array.
$.each(json, function(i) {
supposed to be
$.each(related, function(val, i) {
Also the above line, creates an i that is scoped to the each callback. So there is not need to declare it in the line above.
Here val is the current value in iteration whose index is i. It can be simplified as
$.each(related, function(val, i) {
var sizeClass = JSON.stringify(val.pname);
I have set up a HBS helper which takes in two arrays of objects (users privileges). What I want to do is compare them and inject back into the template the privileges the user does and doesn't have.
Presently I can compare the names of the privileges with the following code:
hbs.registerHelper('selected', function(option, value){
var i;
var j;
var privName;
var userPriv;
var privObj = new Object();
var privArray = [];
for(i in option){
console.log('each ' + JSON.stringify(option[i]));
privName = option[i].privname;
for (y in value){
if(privName == value[y].privname){
userPriv = value[y].privname;
console.log('user has the following privileges', value[y].privname);
privObj = new Object();
privObj.name = userpriv;
privObj.id = value[y]._id;
privObj.state = 'selected';
privArray.push(privObj);
} else if (privName != value[y].privname){
console.log('user doesnt have priv ', privName);
privObj = new Object();
privObj.name = option[i].privname;
privObj.id = option[i].id;
privObj.state = '';
privArray.push(privObj);
}
}
}
console.log('privileges array ', privArray);
return privArray;
});
This works OK when the user only has one privilege, however when the user has more than one, for example two privileges, it returns the privileges twice. If the user has 3, thrice and so on. I know this is because the array is looping again because their are 2, 3 etc in the .length. However I can't seem to find an adequate solution.
Any help?
P.S. it would be nice if the Array.includes() method allowed you to search object properties.
The problem creating new objects the way you did is that for each property you add to your privilege-entity you will have to return to that function and set that property as well. You can instead just add/alter the state property of the existing objects:
hbs.registerHelper('selected', function(option, value) {
var names = option.map(function(opt) {
return opt.privname;
});
value.forEach(function(val) {
val.state = names.indexOf(val.privname) >= 0 ? 'selected' : '';
});
return value;
});
Basically:
The variable names is being mapped to be an array only with the privnames. You can check by using console.log(names).
The Array.forEach() function is helpful in this case because you just need to iterate over each object inside value and set its state-property.
To check if the privname exists, you just need to check the index in the previous names-mapped-array. For such a simple thing I used ternary operator (?:).
Finally, you return value, which is the array containing the objects you had updated.
Let's say we have an array of objects like:
var fruits = [ {name:"banana", weight:150},{name:"apple", weight:95},{name:"orange", weight:160},{name:"kiwi", weight:80} ];
I want to populate a "heavy_fruits" array with items from the "fruits" array above which weight is > 100. Here is my code:
var heavy_fruits = [];
myfruit = {};
fruits.forEach(function(item,index) {
if ( item.weight > 100 ) {
myfruit ["name"] = item.name;
myfruit ["weight"] = item.weight;
}
heavy_fruits.push(myfruit);
});
However it shows:
name:"orange", weight:160
name:"orange", weight:160
name:"orange", weight:160
name:"orange", weight:160
I know this is an issue with mixing closures and loops... but I read an article (http://zsoltfabok.com/blog/2012/08/javascript-foreach/) explaining that I would avoid this kind of issue using a forEach loop instead of the classic for loop.
I know I can use array methods like filter(), etc. but I'm asking that on purpose since I'm actually having troubles with a much bigger function that I cannot expose here... So I tried to summarize and simplify my issue description with "fruits".
var heavy_fruits = [];
myfruit = {}; // here's your object
fruits.forEach(function(item,index) {
if ( item.weight > 100 ) {
myfruit ["name"] = item.name;
myfruit ["weight"] = item.weight; // you modify it's properties
}
heavy_fruits.push(myfruit); // you push it to the array
});
You end up with an array [myfruit, myfruit, myfruit, myfruit].
Now if you modify myfruit anywhere in the code, the change will be visible in every single occurence of myfruit. Why?
Because you are modifying the referenece to the object.
In this example, your array stores just copies of your object. And if you change one of it, every single one will change, because they are all references.
To fix this with each iteration you should be creating a new object and then doing some stuff on it.
BTW, as a matter of fact, your if could just be like this:
if ( item.weight > 100 ) {
heavy_fruits.push(item); // if `item` only has `name` and `weight` properties
}
fruits.forEach(function (item, index) {
if (item.weight > 100) {
myfruit = {};
myfruit["name"] = item.name;
myfruit["weight"] = item.weight;
heavy_fruits.push(myfruit);
}
});
The shorter would use filter
var heavy_fruits = fruits.filter(x => x.weight > 100);
But if you realy want to use forEach do this way
var heavy_fruits = [];
fruits.forEach(x => {if(x.weight > 100) heavy_fruits.push(x)} );
I have an array of Exclusions like below:
Exclusions: [ID:"233242", Loc:"West", ID:"322234" , Loc:"South"]
I also Object nested with an array of objects that could look something like
Schools : [ O: [ ID:"233242" ] , 1:[ ID:"233242"] , 2: [ID :"954944"] ]
I need to delete from the schools object any matching array indices with the same ID but only for the first match. That means element 0 should be removed, but element 1 should still be there. What's the best way to fix my loop:
$.each(Exclusions, function (index, value) {
var loc = value.Loc;
var ID = value.ID;
Object.keys(Schools.District.Pack[loc]).forEach(function (key) {
//i need to scan through the entire object
if (Schools.District.Pack[loc].ID === ID) {
//remove the first match now stop looking
Schools.District.Pack[loc].splice(key, 1);
//break ; incorrect
}
});
});
I'd say having another lookup array for removed IDs, and you'll need something like this
var Exclusions = [{ID:"233242", Loc:"West"}, {ID:"322234" , Loc:"South"}];
var Schools = [{ ID:"233242" } ,{ ID:"233242"} , {ID :"954944"} ];
var removedKeys = [];
$.each(Exclusions, function (index, value) {
var loc = value.Loc;
var ID = value.ID;
Object.keys(Schools).forEach(function (key) {
//i need to scan through the entire object
if ((Schools[key].ID === ID) && (removedKeys.indexOf(ID) == -1)) {
removedKeys.push(ID);
//remove the first match now stop looking
delete Schools[key];
}
});
});
console.log(removedKeys);
console.log(Schools);
Hope this would help
fiddle
I have this 2D array as follows:
var data = [[1349245800000, 11407.273], [1349247600000, 12651.324],
[1349249400000, 11995.017], [1349251200000, 11567.533],
[1349253000000, 11126.858], [1349254800000, 9856.455],
[1349256600000, 8901.779], [1349258400000, 8270.123],
[1349260200000, 8081.841], [1349262000000, 7976.148],
[1349263800000, 7279.652], [1349265600000, 6983.956],
[1349267400000, 7823.309], [1349269200000, 6256.398],
[1349271000000, 5487.86], [1349272800000, 5094.47],
[1349274600000, 4872.403], [1349276400000, 4168.556],
[1349278200000, 4501.939], [1349280000000, 4150.769],
[1349281800000, 4061.599], [1349283600000, 3773.741],
[1349285400000, 3876.534], [1349287200000, 3221.753],
[1349289000000, 3330.14], [1349290800000, 3147.335],
[1349292600000, 2767.582], [1349294400000, 2638.549],
[1349296200000, 2477.312], [1349298000000, 2270.975],
[1349299800000, 2207.568], [1349301600000, 1972.667],
[1349303400000, 1788.853], [1349305200000, 1723.891],
[1349307000000, 1629.002], [1349308800000, 1660.084],
[1349310600000, 1710.227], [1349312400000, 1708.039],
[1349314200000, 1683.354], [1349316000000, 2236.317],
[1349317800000, 2228.405], [1349319600000, 2756.069],
[1349321400000, 4289.437], [1349323200000, 4548.436],
[1349325000000, 5225.245], [1349326800000, 6261.156],
[1349328600000, 8103.636], [1349330400000, 10713.788]]
How do I get the index of value 1349247600000 in the array? I have tried $.inArray(1349247600000, data) but as expected this fails. Is there any other way or do I have to iterate over each? I am reluctant to add another loop to my process
This is a typical performance versus memory issue. The only way (that I know of) to avoid looping through the array, would be to maintain a second data structure mapping the timestamps to the index of the array (or whatever data might needed).
So you would have
var data = [
[1349245800000, 11407.273],
[1349247600000, 12651.324],
// ...
[1349330400000, 10713.788]
];
// the timestamps pointing at their respective indices
var map = {
'1349245800000': 0, // 0
'1349247600000': 1, // 1
// ...
'1349330400000': 42, // n - 1 (the length of the data array minus one)
}
This way, you use more memory, but have a constant lookup time when needing the index of the item in the array that a given timestamp belongs to.
To get the index of a given timestamp do:
map['1349247600000']; // resulting in 1 (e.g.)
If the data structure is dynamically changed, you would of course need to maintain the map data structure, but depending on the context in which you need the lookup, the constant time lookup can potentially be a real time saver compared to a linear time lookup.
I think you need a different data structure.
Try using a standard javascript object ({ key: value } - sometimes called a map or dictionary) to express your data. Looking up keys in an object is highly optimized (using something called hash tables).
If the index in your array has any meaning, store it as a property (typically named _id).
Ideally you should be using an object for this:
var data = {
'1349247600000': 12651.324
}
which you can access like:
data['1349247600000'];
However, this might be a nice solution (IE9 and above) in the meantime:
var search = 1349247600000;
function findIndex(data, search) {
var filter = data.filter(function (el, i) {
el.unshift(i);
return el[1] === search;
});
return filter[0][0];
}
console.log(findIndex(data, search));
fiddle : http://jsfiddle.net/CLa56/
var searchElement = 1349251200000;
var strdata = data.toString();
var newdata = eval("[" + strdata + "]");
var indexsearch = newdata.indexOf(searchElement);
var index = indexsearch/2; // 2 because array.length = 2
var params = {id: 1349251200000, index: -1};
data.some(function (e, i) {
if (e[0] === this.id) {
this.index = i;
return true;
}
}, params);
console.log(params.index);
jsfiddle
MDN|some Array Method
Note that this solution stops iterating after found, not necessarily over the entire array, so could be much faster for large arrays.
What about a custom cross browser solution ?
function findIndexBy(a, fn) {
var i = 0, l = a.length;
for (; i < l; i++) {
if (fn(a[i], i)) {
return i;
}
}
return -1;
}
Usage :
var list = [[1],[2],[3]], idx;
// idx === 1
idx = findIndexBy(list, function (item, i) {
return item[0] === 2;
});
// idx === -1
idx = findIndexBy(list, function (item, i) {
return item[0] === 4;
});