I'm given the task to deduce and explain the lines of codes of a counter program. Below are the codes to the program which is working perfectly. I have included my explanations as comments in the code but it seems my explanation for the last 4 lines of codes (starting from ... "if(counter[index][entry] === undefined){...}") doesn't really explain it.
Can anyone please read the codes and give me a better explanation to them especially why we equate "counter[index][entry] = 1".
<script>
//an array containing a list of objects with sub arrays that has to be
//counted "separately"
var arr = [
{"gateways":["ccu1"],"manufacturer":["homematic"],"ir":["ir_no"],"ip":
["ip_cam","ip_other"]},
{"gateways":["v3"],"manufacturer":["homematic"],"ir":["ir_no"],"ip":
["ip_cam"]},
{"gateways":["v2","v3","v4","ccu2"],"manufacturer":
["homematic","intertechno"],"ir":["ir_yes"],"ip":
["ip_cam","ip_other"]},
{"gateways":["v2","ccu1","ccu2"],"manufacturer":["homematic"],"ir":
["ir_yes"],"ip":["ip_cam","ip_other"]},
{"gateways":["gw_none"],"manufacturer":["homematic"],"ir":
["ir_no"],"ip":["ip_cam"]},
{"gateways":["v3","ccu2"],"manufacturer":
["homematic","fs20","intertechno","elro","Eltako Enocean"],"ir":
["ir_yes"],"ip":["ip_cam","ip_other"]},
{"gateways":["v3","v4"],"manufacturer":["homematic"],"ir":
["ir_no"],"ip":["ip_other"]},
{"gateways":["v3","v4"],"manufacturer":["homematic"],"ir":
["ir_no"],"ip":["ip_other"]},
{"gateways":["v2"],"manufacturer":["intertechno"],"ir":["ir_yes"],"ip":
["ip_other"]},
{"gateways":["v4"],"manufacturer":["homematic","intertechno"],"ir":
["ir_yes"],"ip":["ip_cam","ip_other"]}
];
//console.log(arr.length);
//first we create an empty array "counter" to contain the separately
//counted objects
var counter = [];
//we then use "for loop" to loop through the "arr" array to access the
//first index
for(var i=0; i<arr.length; i++) {
//console.log(arr[i]);
// we create a variable "obj" to store the first index object of
//the "arr" array and because it is a loop,
//it will loop till the last object
var obj = arr[i];
//so if we console log "obj", it will display the entire indexes in
//the "arr" array including keys
//console.log(obj);
//we then use "for in" loop to access all the keys in the variable
// "obj" because we wanna count the number
//of all respective sub arrays
for(var index in obj) {
//so if we console log "index", it will display the entire keys in
// the "obj" variable; ie:
//in every object, it will run 4X to access all the keys
//console.log(index);
//in the next two lines of codes, we have to check if the keys in
// our counter array already exist because
//this is where we gonna put or store our counted respective sub
//arrays. if it doesn't exit, we create it.
if(counter[index] === undefined) {
counter[index] = [];
}
//so if we console log "counter[index]", it will show empty
//arrays which is gonna contain the respective key counts
//console.log(counter[index]);
//next we save the respective arrays to be counted without the
// keys in a variable "arr2".
var arr2 = obj[index];
//console.log(arr2);
//now we wanna loop through the "arr2" array because it
//contains the entries (in arrays) we wanna count
///starting from the first index to the last
for(var j = 0; j < arr2.length; j++) {
//we then store the single entries in a variable called "entry"
//(not with keys or in an array)
var entry = arr2[j];
//console.log(entry);
//in the next two lines of codes, we have to check if the keys
//in our counter array exist because this is where
//we would count "entry"
if(counter[index][entry] === undefined) {
counter[index][entry] = 1;
//console.log(counter[index][entry]);
} else {
counter[index][entry]++;
}
}
}
}
console.log(counter);
</script>
If counter[index][entry] is undefined, it means that we haven't began counting it yet. So we need to start counting it. So we count once, which sets the counter to 1.
If counter[index][entry] does exist (the else clause), it means that there's already a number in counter[index][entry] (because that's what we initialize it to if it doesn't), so just increment the number by one.
Another, perhaps clearer way of writing this would be:
if (counter[index][entry] === undefined) {
counter[index][entry] = 0; // If it doesn't exist, initialize counter to 0.
}
counter[index][entry]++; // Here the counter is a number for sure, increment once.
Basically it's trying to count how many each specific property exists. Eg, how many occurences of gateway v2, how many times ip_other exists etc.
The part with 'if(counter[index] === undefined)' is weird. Counter is an array. And index is the key from an object (namely gateway, manufacturer, ip and ir) So basically it's trying to set keys on an array. Although this is valid javascript, counter should be an object instead of an array to be 'more logical'. Hence the code looks a bit confusing.
Anyways, the rest works like madara posted.
Related
I'm trying to loop over multiple arrays using a single "for in" or "for each" loop.
I have three arrays namely, name, id , and available. The size of these arrays are equal.
What I need to do is that I need to iterate through each value of the above arrays and based on the index of the row and column (values of i and j, respectively), copy the value of the element to a cell in spreadsheet.
The below is the code I am using:
for (i = 1; i <= copy_range.getNumRows(); i++) {
for (j = 1; j <= copy_range.getNumColumns(); j++) {
if (j == 1) {
var name_cell = copy_range.getCell(i, j);
// I want to do this however I'm not able to do this since I already have i and j for
// row and column iteration and that another nested loop makes things complicated
name_cell.setValue(name[k]);
}
else if (j == 2) {
var id_cell = copy_range.getCell(i, j);
Logger.log(id_cell.getA1Notation());
id_cell.setValue(id[k]); //Same idea as in previous if
}
else {
var availability_cell = copy_range.getCell(i, j);
Logger.log(availability_cell.getA1Notation());
availability_cell.setValue(available[k]); //Same as if and else if loops previously.
}
}
The reason I'm not able to use indices is that I already use i and j as iterative variables to refer to row and column, and using another nested loop does not give me the intended output - it leads to unwanted iterations and execution time.
Please let me know if there is any way where I can use a "for in" or a similar loop to iterate over each item of all the three arrays.
To me it seems like you have three "column" arrays of N entries and want to write them to a range that is N rows and 3 columns (named copy_range). The best way to do this is to join these 3 arrays into the requisite 2D array that can be written directly in a single call:
const output = name.map(function (nameVal, row) {
return [nameVal, id[row], availability[row]];
});
copy_range.setValues(output);
The above uses the Array#map method to iterate the name array, and assigns the index associated with each element nameVal to be row. The corresponding element in the id and availability arrays is then accessed using row.
Array#map
I am quite new to JS and there is a problem with my code that I really don't understand: I try to append a text-value pair to an array and basically it works, but instead of appending a single pair, it appends two pairs. Below is the code snippet and the problem is in the latter if-statement. I have added comments on to the snippet to explain what I am doing.
players.addResult = function(result){
//result-argument contains results of games, like: Object {0: "4-2", 1: "5-3", 2: "7-1"}
var playerList = [];
var standingArray = [];
resultLen = Object.keys(result).length;
//loop iterates thru results by index
for (var x=0; x < resultLen; x++) {
var newResult = result[x];
newResult = newResult.split("-");
if (newResult[0] > newResult[1]) {
//the next line retrieves the name of a winning player from another array
playerVal = players.pairs[x].player1;
playerVal = playerVal.text;
console.log(playerVal); //Output of this: Bill
value = playerList.indexOf(playerVal); // Checks if the player already exists on the standings, returns -1 if false.
if (value === -1) {
/*if the player gets his first points, his name and earned
points are added on to the standingArray. And the next line of code is the
interesting one, because I am expecting to have an array with
index, name of the winner and points. Like this:
Object 0 name:"Bill" points:2. But instead of that, there are two
objects on the array, one for both players:
[Object]
0: Object
name: "Bill"
points: 2
__proto__: Object
1: Object
name: "Greg"
points: 2
__proto__: Object
length: 2
__proto__: Array[0]*/
standingArray.push({name: playerVal, points: 2})
playerList.push(playerVal); //this is for keeping on track that player is now on the standingArray
console.log(playerVal);
console.log(playerList);
console.log(standingArray);
}
else {
console.log("else");
}
console.log(playerList);
console.log(standingArray);
So the question is that how JS gets Greg's name - I have checked the playerVal-variable and it really contains only Bill's name. Any help is warmly welcome!
The output that appears in the console from console.log is alive. It reflects changes to the objects passed to console.log after the console.log call has occurred.
You might want to JSON.stringify before passing the object to console.log or make a copy of the Array:
console.log(playerList.slice());
console.log(standingArray.slice());
I am working on an exercise where I prompt the user for a list of names, store the list of names in an array, sort the array in ascending order, and print the list of names (one per line). When I do so, I see a numeric value displayed instead of one name per line. Why is this happening?
var namesArray = [];
do {
var names = prompt("Enter a name: ");
namesArray.push(names);
} while (names != "")
namesArray.sort();
for (var name in namesArray) {
document.write(name);
}
When you use this construct:
for (var name in namesArray) {
the value of name will be the index in the array (the property name). If you want the actual value in the array, you have to use that property name/index to get the value:
document.write(namesArray[name]);
Of course, you really should not iterate arrays that way in the first place because that iterates all the enumerable properties of the array object (potentially including non array elements) as you can see in this example. Instead, you should use a traditional for loop as in this code example that follows:
var namesArray = [];
do {
var names = prompt("Enter a name: ");
namesArray.push(names);
} while (names != "")
namesArray.sort();
for (var i = 0; i < namesArray.length; i++) {
document.write(namesArray[i]);
}
Other options for iterating the array:
namesArray.forEach(function(value) {
document.write(value)
});
Or, in ES6, you can use the for/of syntax which does actually work how you were trying to use for/in:
for (let value of namesArray) {
document.write(value);
}
You also may want to understand that using document.write() after the document has already been parsed and loaded will cause the browser to clear the current document and start a new one. I don't know the larger context this code fits in, but that could cause you problems.
First, in a for..in loop, here name represents the keys and not the values in your array (you should use namesArray[name])
Also there is another important thing to note. An array is not recommended to be looped through using for..in and if so, you should do it like this:
for (var key in array) {
if (array.hasOwnProperty(key)) {
// then do stuff with array[key]
}
}
The usual preferred ways to loop through an array are the following:
A plain for loop
for (var i = 0, l = array.length; i < l; i++) {
// array[i]
}
Or a higher order function with Array.prototype.forEach (IE9+ if you need compat with IE)
array.forEach(function (item) {
// do something with the item
});
If i declare this
var data = [];
data [300] = 1;
data [600] = 1;
data [783] = 1;
I have an array of length 784 but with only 3 defined items within it.
Since splice(300,1) would delete the item and the index but would also shift every consecutive position, how can i delete the object in the index 300 from the array without altering the order of the array so when i use
for(var x in data)
it can correctly iterate only 2 times, on the indexes 600 and 783?
i tried using data[300] = undefined but the index 300 was still iterated over.
You could use delete:
delete data[300];
This sets the value of the index to be undefined, but doesn't modify the element index itself.
See more about the delete operator here.
dsg's answer will certainly work if you're going to use an array. But, if your data is going to be as sparse as it is, I wonder if a plain Javascript object might be a better choice for such sparse data where you never want the index/key to change. Arrays are optimized for consecutive sequences of data starting with an index of 0 and for iterating over a sequence of values. And, they keep track of a .length property of the highest index used minus one.
But, since you aren't really doing any of that and given the way you are storing data, you aren't able to use any of the useful features of an array. So, you could do this instead with a plain Javascript object:
var data = {};
data [300] = 1;
data [600] = 1;
data [783] = 1;
delete data[300];
This would create an object with three properties and then would delete one of those properties.
You can then iterate over the properties on an object like this:
for (var prop in data) {
console.log(data[prop]);
}
A couple things to remember: 1) The property names are always strings so your numbers would show us as "600" in the prop variable in the above iteration. 2) Iterating with the for/in technique does not guarantee any order of the properties iterated. They could come in any order since properties on an object have no innate order.
You can delete that element from the array:
delete data[300];
The full example:
var data = [];
data [300] = 1;
data [600] = 1;
data [783] = 1;
delete data[300];
var result = "";
for (var x in data) {
result += "<div>" + x + "</div>";
}
document.getElementById("output").innerHTML = result;
<div id="output" />
Consider:
var main = []
Now I want to generate many (289 to be exact) Arrays to be elements in the main one. Each of these arrays will have be like:
var subobject = {x:"A", y:"B", terrain:"C", note:"D"}
Generating the values are no problem, and I can easily put those values in a already defined subobject = {} and push(), but I can't figure out how to iterate a script each time which creates a new object and then push() into var main.
The naming of the subobject is unimportant, I'm looking for solution inwhich I can pull specific information such as:
main[0].x // get x value of the subarray in 0 location in main
main[5].note// get note value of the subarray in 5 location in main
(would it make a difference if every array had the same name? since I would never access subobject directly (after being pushed into main), but through main[X].YYY or would it have to be via main[X].subarray[Y] ?)
for (var i = 0; i < 289; i++) {
main.push({x: getRandomX(), y: getRandomY(), terrain: getTerrain(), note: ""});
}
as long as you create new objects {} before you push them to the array it is ok.
it doesn't matter if you assign the new object to the same variable (ie subobject)
you access them later like this:
main[0].x // get the value of x of the first element
[x:"A", y:"B", terrain:"C", note:"D"] isn't valid javascript, I think you want an object here:
{x:"A", y:"B", terrain:"C", note:"D"}
And to push each generated value, you can use a for loop
for (var i = 0; i < 9; i++) {
//do something, for example, generate a value
}
Arrays are only numerically indexed.
If you want named keys you have to use objects.
Here's the wrong way to do it.
var main = [],
subobject = {x:"A", y:"B", terrain:"C", note:"D"};
for(var i=0; i<289; i++){
subobject["x"] = Math.random();
subobject["terrain"] = Math.random();
//continue adding values using keys
main.push(subobject);
}
The thing is if you just use the same object your going to access that object every time you iterate it, and you'll replace it's value.
So you should do it like this.
var main = [],
subobject = {};
for(var i=0; i<289; i++){
subobject = {};//new object to make for uniquness
subobject["x"] = Math.random();
subobject["terrain"] = Math.random();
//continue adding values using keys
main.push(subobject);
}
You access members like this.
main[0].x;//value of x at index 0
//next index
main[1].terrain;//value of terrain at index 1
Collisions will only happen if you set the same index twice.
main[2].x = "value";
main[2].x = "replace value by accident";
Unless you want to change the value for some reason.
A different index will always give you a different object if you set a different one each time.