Iterating through multiple arrays in Javascript without using indices - javascript

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

Related

Explanation to a given Code in Javascript

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.

JavaScript returning numeric value instead of string(s) after sorting array

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
});

Retrieve Index Number of Unique Elements

for (let i = 0; i < arrayItemsLen; i++) {
let uniqueItems = arrayItems.filter(function(item, i, arrayItems) {
return i == arrayItems.indexOf(item);
});
}
This method retrieves unique items in the arrayItems to uniqueItems array. What I want to do is also get the index numbers of each unique element and assign it to another temp array. I can't find a way to achieve that.
E.g.: arrayItems.indexOf(item) gives the index of each unique element, but then, how do I get that index to the tempArray[i], I guess I need a for loop but I really don't know where to put it.
I would use something like
var uniqueIndices = [],
uniqueValues = [];
for(var i=0; i<array.length; ++i) {
if(uniqueValues.indexOf(array[i]) >= 0) continue;
uniqueIndices.push(i);
uniqueValues.push(array[i]);
}
Basically, it iterates array checking if the current value is already in uniqueValues. If it's there, it does nothing and continues to the next iteration. Otherwise, it adds the value to uniqueValues and the key to uniqueIndices.
Using i == array.indexOf(array[i]) like in your question would also work, but it may be slower because array will be bigger than uniqueValues.

Removing an Index altogehter from a javascript array

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" />

Loop to remove an element in array with multiple occurrences

I want to remove an element in an array with multiple occurrences with a function.
var array=["hello","hello","world",1,"world"];
function removeItem(item){
for(i in array){
if(array[i]==item) array.splice(i,1);
}
}
removeItem("world");
//Return hello,hello,1
removeItem("hello");
//Return hello,world,1,world
This loop doesn't remove the element when it repeats twice in sequence, only removes one of them.
Why?
You have a built in function called filter that filters an array based on a predicate (a condition).
It doesn't alter the original array but returns a new filtered one.
var array=["hello","hello","world",1,"world"];
var filtered = array.filter(function(element) {
return element !== "hello";
}); // filtered contains no occurrences of hello
You can extract it to a function:
function without(array, what){
return array.filter(function(element){
return element !== what;
});
}
However, the original filter seems expressive enough.
Here is a link to its documentation
Your original function has a few issues:
It iterates the array using a for... in loop which has no guarantee on the iteration order. Also, don't use it to iterate through arrays - prefer a normal for... loop or a .forEach
You're iterating an array with an off-by-one error so you're skipping on the next item since you're both removing the element and progressing the array.
That is because the for-loop goes to the next item after the occurrence is deleted, thereby skipping the item directly after that one.
For example, lets assume item1 needs to be deleted in this array (note that <- is the index of the loop):
item1 (<-), item2, item3
after deleting:
item2 (<-), item3
and after index is updated (as the loop was finished)
item2, item3 (<-)
So you can see item2 is skipped and thus not checked!
Therefore you'd need to compensate for this by manually reducing the index by 1, as shown here:
function removeItem(item){
for(var i = 0; i < array.length; i++){
if(array[i]==item) {
array.splice(i,1);
i--; // Prevent skipping an item
}
}
}
Instead of using this for-loop, you can use more 'modern' methods to filter out unwanted items as shown in the other answer by Benjamin.
None of these answers are very optimal. The accepted answer with the filter will result in a new instance of an array. The answer with the second most votes, the for loop that takes a step back on every splice, is unnecessarily complex.
If you want to do the for loop loop approach, just count backward down to 0.
for (var i = array.length - 0; i >= 0; i--) {
if (array[i] === item) {
array.splice(i, 1);
}
}
However, I've used a surprisingly fast method with a while loop and indexOf:
var itemIndex = 0;
while ((itemIndex = valuesArray.indexOf(findItem, itemIndex)) > -1) {
valuesArray.splice(itemIndex, 1);
}
What makes this method not repetitive is that after the any removal, the next search will start at the index of the next element after the removed item. That's because you can pass a starting index into indexOf as the second parameter.
In a jsPerf test case comparing the two above methods and the accepted filter method, the indexOf routinely finished first on Firefox and Chrome, and was second on IE. The filter method was always slower by a wide margin.
Conclusion: Either reverse for loop are a while with indexOf are currently the best methods I can find to remove multiple instances of the same element from an array. Using filter creates a new array and is slower so I would avoid that.
You can use loadash or underscore js in this case
if arr is an array you can remove duplicates by:
var arr = [2,3,4,4,5,5];
arr = _.uniq(arr);
Try to run your code "manually" -
The "hello" are following each other. you remove the first, your array shrinks in one item, and now the index you have follow the next item.
removing "hello""
Start Loop. i=0, array=["hello","hello","world",1,"world"] i is pointing to "hello"
remove first item, i=0 array=["hello","world",1,"world"]
next loop, i=1, array=["hello","world",1,"world"]. second "hello" will not be removed.
Lets look at "world" =
i=2, is pointing to "world" (remove). on next loop the array is:
["hello","hello",1,"world"] and i=3. here went the second "world".
what do you wish to happen? do you want to remove all instances of the item? or only the first one? for first case, the remove should be in
while (array[i] == item) array.splice(i,1);
for second case - return as soon as you had removed item.
Create a set given an array, the original array is unmodified
Demo on Fiddle
var array=["hello","hello","world",1,"world"];
function removeDups(items) {
var i,
setObj = {},
setArray = [];
for (i = 0; i < items.length; i += 1) {
if (!setObj.hasOwnProperty(items[i])) {
setArray.push(items[i]);
setObj[items[i]] = true;
}
}
return setArray;
}
console.log(removeDups(array)); // ["hello", "world", 1]
I must say that my approach does not make use of splice feature and you need another array for this solution as well.
First of all, I guess your way of looping an array is not the right. You are using for in loops which are for objects, not arrays. You'd better use $.each in case you are using jQuery or Array.prototype.forEach if you are using vanila Javascript.
Second, why not creating a new empty array, looping through it and adding only the unique elements to the new array, like this:
FIRST APPROACH (jQuery):
var newArray = [];
$.each(array, function(i, element) {
if ($.inArray(element, newArray) === -1) {
newArray.push(region);
}
});
SECOND APPROACH (Vanila Javascript):
var newArray = [];
array.forEach(function(i, element) {
if (newArray.indexOf(element) === -1) {
newArray.push(region);
}
});
I needed a slight variation of this, the ability to remove 'n' occurrences of an item from an array, so I modified #Veger's answer as:
function removeArrayItemNTimes(arr,toRemove,times){
times = times || 10;
for(var i = 0; i < arr.length; i++){
if(arr[i]==toRemove) {
arr.splice(i,1);
i--; // Prevent skipping an item
times--;
if (times<=0) break;
}
}
return arr;
}
An alternate approach would be to sort the array and then playing around with the indexes of the values.
function(arr) {
var sortedArray = arr.sort();
//In case of numbers, you can use arr.sort(function(a,b) {return a - b;})
for (var i = 0; sortedArray.length; i++) {
if (sortedArray.indexOf(sortedArray[i]) === sortedArray.lastIndexOf(sortedArray[i]))
continue;
else
sortedArray.splice(sortedArray.indexOf(sortedArray[i]), (sortedArray.lastIndexOf(sortedArray[i]) - sortedArray.indexOf(sortedArray[i])));
}
}
You can use the following piece of code to remove multiple occurrences of value val in array arr.
while(arr.indexOf(val)!=-1){
arr.splice(arr.indexOf(val), 1);
}
I thinks this code much simpler to understand and no need to pass manually each element that what we want to remove
ES6 syntax makes our life so simpler, try it out
const removeOccurences = (array)=>{
const newArray= array.filter((e, i ,ar) => !(array.filter((e, i ,ar)=> i !== ar.indexOf(e)).includes(e)))
console.log(newArray) // output [1]
}
removeOccurences(["hello","hello","world",1,"world"])

Categories

Resources