Calling a javascript array without knowing naming conventions in advance - javascript

I am calling an ajax function that returns a dataset (response) with data column labels. One of the column labels changes depending on where the call was initiated from.
Normally the following code accesses the response variable if the column label is hard coded:
for (var i = 0; i < response.d.length; i++) {
data.setValue(i, 1, response.d[i].Emissions);
}
However, I need to be able to access the response variable using a separate string variable that is passed in since the label changes. Below is my feeble attempt at doing it, but it isn't working. What is the correct syntax for doing that?
var columnLabel = 'Emissions';
for (var i = 0; i < response.d.length; i++) {
data.setValue(i, 1, response.d[i].columnLabel);
}

You need to use the []-operator:
data.setValue(i, 1, response.d[i][columnLabel]);
obj.property is equivalent to obj['property'].

Try this:
for (var i = 0; i < response.d.length; i++) {
data.setValue(i, 1, response.d[i]['Emissions']);
}

Related

How to change the currentText of repeated ComboBox in QML?

As I understand it, it is not possible to directly change the property currentText of a QML ComboBox. Instead, one needs to access it via currentIndex. However, I cannot seem to get it either. The JS code I need to update the text of a ComboBox with is the following:
function fillCombosFromHistory (s, rep1, rep2, replength) {
let u = s.replace(/\s+/g,'').split('&');
let v = [];
for (let i = 0; i < u.length; i++) {
v.push({
key: u[i].split('=')[0],
value: u[i].split('=')[1]
})
}
for (let j = 0; j < v.length; j++) {
for (let k = 0; k < replength; k++) {
if (v[j].key === rep1.itemAt(k).text) {
rep2.itemAt(k).model.currentIndex.text = v[j].value;
}
}
}
}
Here I pass the ids of two repeaters rep1 and rep2, where rep1 repeats Text (as a label) and rep2 repeats ComboBox. They are forcefully of the same length, so only one replength.
PS. I currently get the error TypeError: Value is undefined and could not be converted to an object.
So, I solved the problem. Apparently I was just confused about the properties of ComboBox. I changed the line
rep2.itemAt(k).model.currentIndex.text = v[j].value;
to
rep2.itemAt(k).editText = v[j].value;
and then some logic in other functions to accomodate the change and now everything works. Thanks anyway for the helpful hints!

jquery.sheet trigger formula calculation on cell

So I am loading csv files from a server an inserting js function calls that create tables/sheets with jquery.sheet. Everything works thus far but when I put functions into the list they do not calculate.
The sheets (simplified)data object for the td has this before I modify anything:
Object {td: x.fn.x.init[1], dependencies: Array[0], formula: "", cellType: null, value: "=A2+B2+C2"…}
When I set the formula value it changes to:
Object {td: x.fn.x.init[1], dependencies: Array[0], formula: "=A2+B2+C2", cellType: null, value: "=A2+B2+C2"…}
So I understand how to set formula and value but what i wish to do is trigger an event to auto calculate a cell hopefully based on an "X,Y" co-ordinate, or find out if I am taking the wrong approach.
I dont know if this helps but when I go to edit a cell it will appear as ==A2+B2+C2 not =A2+B2+C2
I would supply my code but because of the C# asp and js interaction it is not short I don't think it would help.
Solved:
Two things are essential to load a formula from a csv file to a jquery.sheet instance and then have it calculate. First is to manually set the objects ["formula"] property, while leaving off the '=' in the beginning because it adds its own. then you must trigger the "calc(s,true)" function with s as the sheets index and in my case I put true as the second parameter because I believe it forces calculations on cells with a function.
var sheets = jQuery.sheet.instance[0];
for (var s = 0 ; s < names.length; s++) {
var sheet = sheets.spreadsheets[s];
for (var k = 1; k < sheet.length; k++) {
var row = sheet[k];
for (var j = 1; j < row.length; j++) {
var col = row[j];
//alert(cell.value);
if (col.value.startsWith("=")) {
col["formula"] = col.value.substring(1, col.value.length);
}
}
}
sheets.calc(s, true);
}
If a better way is found please let me know. I do not think this is very scalable as it is O(n^3).

Add a value to each element in javascript

Is there a good way to add a certain value to each element in an array in javascript? Essentially, this should be a better way of writing the following:
a = [1,2,3,4];
for (i = 0; i < a.length; i++) {
a[i] += 7;
}
Maybe using map (but not necessarily)?
Edit:
Or a more interesting example:
a = [{'x':1},{'x':2},{'x':3},{'x':4}];
for (i = 0; i < a.length; i++) {
a[i].x += 7;
}
You can use map to do it:
a = a.map(function(entry) {
return entry + 7;
});
I'm not seeing how it's "better" to create a new array rather than update the one you have.
You can also use forEach:
a.forEach(function(entry, index) {
a[index] += 7;
});
It's still a bunch of function calls (but that's not a problem), but you have the advantage (over a for loop) of not having to declare the indexing variable, and you're modifying the existing array rather than replacing it.
Edit: Your "most interesting" example says even more that map is not really the best choice.
a.forEach(function(entry) {
entry.x += 7;
});
Yes, you can use .map but it will not modify the array in-place, so you must assign the result to a:
a = a.map(function(x) { return x+7 });

Loop through different properties of a json using a sequence of numbers

I have a JSON with different properties called using a sequence of numbers (BOS1, BOS2, BOS3, BOS4, BOS5...). At a certain point of my code, I want to loop through them.
I know this doesn't work, but will give you an idea of what I'm trying to do:
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < 14; j++) {
data[i].BOS+parseInt(j)
}
}
The code is more complex than that, as you can imagine, but the thing i'm trying to get data[i].BOS1, data[i].BOS2, data[i].BOS3, data[i].BOS4... And I'm not accomplishing to do it.
Thanks in advance!
You have to use the [] operator:
data[i]['BOS' + j]
For that to work (with the rest of your code), the object would have to look like this:
var data = [
{BOS0: "something", BOS1: "something", ... , BOS13: "something},
{BOS0: "something", BOS1: "something", ... , BOS13: "something},
...
];
Note that your loop starts at zero, not 1.

JavaScript addEventListener() not working as expected

I have never used addEventListener(), but I cannot write the HTML equivalent I would like for each <div> I am treating as a button because of the way I am generating content. The equivalent would be:
<div onmousedown="jsItems[someId].toggleImage(someGallery, someIndex);"></div>
What I've been trying is this:
JsTree.prototype.addGalleries = function(inElements) {
// ...unrelated code here removed for StackOverflow...
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
var self = this;
this.jsGalleries[i].buttons[j].addEventListener("mousedown", function() {
self.toggleImage(i, j);
});
}
}
}
Where i counts from 0 to 1 and j counts from 0 to 2 (for both i in this case), i represents someGallery, j represents someIndex, and I could access someId with this.id inside the code above (or with self.id inside addEventListener's function).
The problem is that although clicking on one of these "buttons" (<div>s) does trigger:
JsTree.prototype.toggleImage = function(inGallery, inIndex) {
alert(this.id+", "+inGallery+", "+inIndex);
}
that it always alerts "8, 2, 3" regardless of which button is clicked. The "8" is correct but I have no idea why "2" or "3" are alerted. They seem to just be 1 more than what i and j count to (verified by trying j < this.jsGalleries[i].buttons.length-1 which alerts "8, 2, 2").
Edit: someId, someGallery, and someIndex are not real variables, they are junk I made up to try to explain the problem.
This is a classic JS mistake. The problem is that the values of i and j are not captured in any function scope, and your event handlers are asynchronous. That means that when your event handler runs, both for loops have run to completion, thus i == this.jsGalleries.length and j === this.jsGalleries[this.jsGalleries.length - 1].buttons.length.
Try out one of these:
JsTree.prototype.addGalleries = function(inElements) {
// ...unrelated code here removed for StackOverflow...
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
(function(self, innerI, innerJ){
var galleryEl = self.jsGalleries[innerI].buttons[innerJ];
galleryEl.addEventListener("mousedown", function() {
self.toggleImage(innerI, innerJ);
});
})(this, i, j);
}
}
}
Or possibly clearer:
JsTree.prototype.addGalleries = function(inElements) {
// ...unrelated code here removed for StackOverflow...
var addHandler = function(self, i, j){
self.jsGalleries[i].buttons[j].addEventListener("mousedown", function() {
self.toggleImage(i, j);
});
};
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
addHandler(this, i, j);
}
}
}
It's not a problem with addEventListener. This is a common mistake. In order to understand what's going on, I have to explain how closures work.
When you have a loop and a function inside of it:
var i = 5;
while(i--){
setTimeout(function(){
console.log(i);
}, 100);
}
Each function is given a reference to the variable i. That means that they don't retain the value of i at the time you defined them. Again, I'll restate, each function has a reference to the same variable i, not to the value that it had at the time the function was declared. In my example above, all of the setTimeout's are defined asynchronously. The anonymous functions all fire at 100 milliseconds and each one logs the value that's in i at the time that the function was run. In my example, that value would be -1 for all the functions.
There are 2 ways to solve this. I'll show you the easy one first:
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
var self = this;
self.gallery = {i: i, j: j};
this.jsGalleries[i].buttons[j].addEventListener("mousedown", function() {
self.toggleImage(self.gallery.i, self.gallery.j);
});
}
}
Here, you're storing the values on the actual DOM element. These values are equivalent to the values at the time that the loop was run, so the event listener grabs the correct value. Notice I nested the value in an object called gallery. I did this to kind of namespace it. It's not a good idea to store values on elements in the DOM, just in case browsers end up implementing a property with the same name. I feel like gallery is safe enough.
The other option, and probably the best practice, for fixing this is to use closures to your advantage.
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
var self = this;
this.jsGalleries[i].buttons[j].addEventListener("mousedown", (function closure(self, i, j){
return function actualListener(){
self.toggleImage(i, j);
}
})(self, i, j));
}
}
In this case, we create a self executing function (called closure in my example) which runs immediately when we're creating the listener. Let me state it again, this function runs the moment the listener is being added, NOT when it's run. The reason we do this is so we can pass in the values we want to save for later, in this case, self, i, and j. Then, when the event occurs, the function that ACTUALLY gets run is the inner function (called actualListener). actualListener has a copy of all the values stored in its closure at the time that the closure function was run.

Categories