Can't call public method while looping over object array - javascript

I'm starting to play around a bit with OOP in JavaScript and I have an array of simple objects that I'm trying to loop over and call a method on each, however, when I run this under Google Chrome, I get the following exception in the JavaScript debugger:
Uncaught TypeError: Object 0 has no method 'drawHisto'
Simplified code snippet below:
var histograms = [];
var h1 = null;
var h2 = null;
var h3 = null;
function Init() {
h1 = new Histogram(canvas1, "red");
h2 = new Histogram(canvas2, "blue");
h3 = new Histogram(canvas3, "green");
histograms = [ h1, h2, h3];
}
function Histogram(canvas, color) {
// this is my constructor
}
Histogram.prototype.drawHisto = function() {
// I will add code here to draw the histogram
}
function DrawHistograms() {
for (var h in histograms) {
h.drawHisto(); // Throws exception!
}
// h1.drawHisto() <--- this works
}
Any idea what I might be doing wrong? I've simplified the code here a bit, so if you find that the problem must be elsewhere, I can add additional context.
Thank you.

A for in loop in JavaScript does not iterate over an array's values, but rather over an object's keys. Simply use a for loop as usual:
function DrawHistograms() {
for (var i = 0; i < histograms.length; i++) {
histograms[i].drawHisto();
}
}
Or, if compatibility with Internet Explorer 8 and earlier is no issue, you may be able to use Array.forEach:
function DrawHistograms() {
histograms.forEach(function(h) {
h.drawHisto();
});
}

for (var h in histograms) {
histograms[h].drawHisto();
}
The for-in-loop in Javascript can be surprising at first: it doesn't loop over the values of the array, but rather the keys. For a straight-up array, I tend to prefer the more verbose but clearer standard for-loop:
for (var i = 0; i < histograms.length; i++) {
histograms[i].drawHisto();
}
Fun fact time! The for-in-loop can be handy for iterating over key-value mappings like {foo: 'bar', spam: 'eggs'}, but beware: it'll iterate over inherited keys, as well. For example, if some wiseguy decided to declare Object.prototype.myMethod, you'd see the keys foo, spam, and myMethod appear. The hasOwnProperty method can make the loop safe, though:
for (var key in obj) {
if(obj.hasOwnProperty(key)) {
// proceed with confidence
}
}

Related

Looking for matches in different arrays (Google Apps Script)

I have the following script in Google Apps Script:
for(var i=0; i<lastCode; i++) {
var productCode = prodCodesArr[i];
for(var j=0; j<kelliLastCode; j++) {
var kelliProductCode = kelliCodesArr[j];
if(productCode == kelliProductCode) {
Logger.log('match found')
}
}
}
The 2 arrays are created dynamically. So the idea is (and I know there must be MUCH better ways to do this, but I am pretty new to this so bear with me) that I am setting i to the value of the first product code in one array and then looping through the other array whilst storing the product codes in this one to j. Now, I tried logging:
Logger.log(productCode + ' - ' + kelliProductCode);
And this worked and indeed, there were instances where productCode and kelliProduct code matched.
Yet my if statement above does not pick these up.
Again, I'm sure I've botched this entirely but any help would be greatly appreciated...
What's the point of the check? To determine which of your prodCodesArr items are also in kelliCodesArr? Why not parse kelliCodesArr just once, and then use hash lookups instead of array traversal? This will mean that you don't have to use nested for loops, which will scale very poorly as the inner loop size grows. An example (with some checks for assumptions on my part):
function foo() {
const kelliCodes = getKelliCodesArraySomehow();
const productCodes = getProductCodesArraySomehow();
// If these are 2D arrays, note that for `var a = ['help']; var b = ['help'];`
// `a` is never equal to `b` because they are not the exact same object in memory.
if (kelliCodes.length && Array.isArray(kelliCodes[0])) {
throw new TypeError("This SO answer was predicated on `kelliCodes` and `productCodes` being 1D arrays, but they aren't!");
}
const kelliLookup = kelliCodes.reduce(function (obj, kpc, idx) {
if (typeof kpc === 'object') {
console.log({message: "This SO answer assumed kpc was a string", kpc: kpc});
throw new TypeError("You probably want to store a property of this object, not the whole object");
}
obj[kpc] = idx;
return obj;
}, {});
var productsAlsoInKelliCodes = productCodes.filter(function (pc) {
return kelliLookup.hasOwnProperty(pc);
});
productsAlsoInKelliCodes.forEach(function (pc) {
Logger.log("The index of this product code %s in kelliCodes is %s", pc, kelliLookup[pc]);
});
}
If your ___codes arrays are 2D arrays, you should flatten them before comparison, as comparing an Array instance to another Array instance will always return false, even if they contain the same element primitives--they aren't the exact same Array instance:
References
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
Array#forEach
Array#map
In JS, which is faster: Object's "in" operator or Array's indexof?
Javascript: what lookup is faster: array.indexOf vs object hash?
I'm sure there are more.
Something like this might help you to see what's happening:
function compareA(prodCodesArr,kelliCodesArr) {
var html="";
for(var i=0;i<prodCodesArr.length;i++) {
for(var j=0;j<kelliCodesArr.length;j++) {
if(productCodesArr[i]==kelliCodesArr[j]) {
html+=Utilities.formatString('Matched: %s=%s', productCodesArr[i],kelliCodesArr[j]);
}else{
html+=Utilities.formatString('No-Match: %s=%s', productCodesArr[i],kelliCodesArr[j]);
}
}
}
var userInterface=HtmlService.createHtmlOutput(html);
SpreadsheetApp.getUi().showModelessDialog(userInterface, 'Comparing')
}

JS multidimensional array spacefield

i wanna generate a 3x3 field. I want to do this with JS, it shall be a web application.
All fields shall inital with false. But it seems so that my code is not working correctly, but i don't find my fault. The goal is, that every spacesector is accessible.
Thats my idea:
// define size
var esize = generateSpace(3);
}
space[i] = false is replacing the array with a single boolean value false, not filling in all the entries in array you just created. You need another loop to initialize all the elements of the array.
function generateSpace(x) {
var space = [];
for (var i = 0; i < x; i++) {
space[i] = [];
for (var j = 0; j < x; j++) {
space[i][j] = false;
}
}
return space;
}
Also, your for() loop condition was wrong, as you weren't initializing the last element of space. It should have been i < space.length.
And when it's done, it needs to return the array that it created.
Since I got somewhat bored and felt like messing around, you can also initialize your dataset as shown below:
function generateSpace(x) {
return Array.apply(null, Array(x)).map(function() {
return Array.apply(null, Array(x)).map(function() {
return false;
});
});
}
The other functions work equally well, but here's a fairly simply looking one using ES6 that works for any square grid:
function generateSpace(x) {
return Array(x).fill(Array(x).fill(false));
}

Mootools is adding unwanted function calls to JSON results

I have a function that gets a list of vehicles in an asp.net page
function GetVehicleCounts(totalVehicles, vehiclesInDepot, vehiclesInFilter, vehiclesInKnownLocations, vehiclesInUnknownLocations, vehiclesNotInDepot)
{
vehiclesInDepot.value = 0;
vehiclesInFilter.value = 0;
vehiclesInKnownLocations.value = 0;
vehiclesInUnknownLocations.value = 0;
vehiclesNotInDepot.value = 0;
var listofVehicles;
var count = 0;
for (var k in vehicles_dm) {
vehiclesInDepot.value++;
if (vehicles_dm[k].IsInFilter) {
vehiclesInFilter.value++;
}
if (vehicles_dm[k].CurrentFeatureType == 11) {
vehiclesInUnknownLocations.value++;
}
else {
vehiclesInKnownLocations.value++;
}
}
if (vehicles_dm != null) {
vehiclesNotInDepot.value = totalVehicles - vehicles_dm.length;
}
}
However, when I add Mootools to the page I run into the problem of Mootools adding all the function calls to the results. This ends up getting repeated for any function that is similar. Any ideas for correcting?
I don't have a choice about using Mootools and the existing page is done in jQuery.
I found the answer!
'for..in is not meant for array iteration. It iterates over all the properties of an object that are not built-in. Since MooTools has added more functions to Array prototype, they are now array properties as well. See this https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Statements/For...in
Just use a basic for loop for array iteration.'
Javascript array iteration using for..in with MooTools included

Looping through Photoshop layers in Javascript

I'm trying to write a Photoshop script that will show all layers of a given name. I need to loop through all the possible nested layer sets and am using the following code:
function showBounds(layerNode)
{
for(var layer in layerNode.artLayers)
{
if (layer.name == "#bounds")
{
layer.visible = 1;
}
}
showBounds(layerNode.layerSets);
}
showBounds(app.activeDocument.doc.layerSets);
But when I run it, I get the following error:
Error 1302: No such element
Line: 5
-> for(var layer in layerNode.artLayers)
artLayers should be a property of LayerSets, so I'm confused.
This is also my first attempt at scripting PS (and using javascript), so there might be some fundamental concept I am not getting.
I think you need something more like:
function showBounds(layerNode) {
for (var i=0; i<layerNode.length; i++) {
showBounds(layerNode[i].layerSets);
for(var layerIndex=0; layerIndex < layerNode[i].artLayers.length; layerIndex++) {
var layer=layerNode[i].artLayers[layerIndex];
if (layer.name == "#bounds") {
layer.visible = 1;
}
}
}
}
showBounds(app.activeDocument.layerSets);
Also, javascripts for...in syntax doesn't work the way you think it does. It's not like a foreach loop. It loops over the property names of an object.

Create an array with tree elements in Javascript

I need to create an array from tree elements in Javascript and being a newbie I don't know how to achieve this.
pseudo-code :
function make_array_of_tree_node(tree_node)
{
for (var i = 0; i < tree_node.childCount; i ++) {
var node = tree_node_node.getChild(i);
if (node.type ==0) {
// Here I'd like to put a link (node.title) in an array as an element
} else if (node.type ==6) {
// Here the element is a folder so a I need to browse it
make_array_of_tree_node(node)
}
}
}
// Some code
make_array_of_tree_node(rootNode);
// Here I'd like to have access to the array containing all the elements node.title
You can declare an array like this:
var nodes = [];
Then you can add things to it with:
nodes.push(something);
That adds to the end of the array; in that sense it's kind-of like a list. You can access elements by numeric indexes, starting with zero. The length of the array is maintained for you:
var len = nodes.length;
What you'll probably want to do is make the array another parameter of your function.
edit — To illustrate the pattern, if you've got a recursive function:
function recursive(data, array) {
if ( timeToStop ) {
array.push( data.whatever );
}
else {
recursive(data.subData, array);
}
}
Then you can use a second function to be the real API that other code will use:
function actual(data) {
var array = [];
recursive(data, array); // fills up the array
return array;
}
In JavaScript, furthermore, it's common to place the "recursive" function inside the "actual" function, which makes the recursive part private and keeps the global namespace cleaner.

Categories