Compare columns and rows for duplicates - arrays

Pool Reservations:
I am attempting to create a sheet for pool reservation. Column A dictates Time and Lap lane. I.e 5:15 AM 1 is for lap lane one. Column E is for the date. The time and lap lane can be scheduled for multiple days but the time/lap lane cannot be scheduled twice for the same day. I would like to highlight a row red if columns A and E within a row match A and E within a different row. In the example, both rows 3 and 6 should be highlighted red.

So right now you have three columns and you want to check if there are any duplicates in that columns.
Something like this:
I made this simple script:
function myFunction() {
const sheet = SpreadsheetApp.getActiveSheet();
const range = sheet.getRange("A1:C8");
const values = range.getValues();
for(let i = 0; i < values.length; i++){
let row = values[i];
for(let j=0; j < values.length; j++){
// Check if there are any row (excluding the current one) with the same values
if(j != i && JSON.stringify(row) === JSON.stringify(values[j])){
sheet.getRange(i+1, 1, 1, row.length).setBackground("red");
}
}
}
}
Basically I would get all the rows inside the range variable and check for every single one of them to see if there are any row repeating. I think the code itself is very self explanatory the only thing I think is worth mentioning is comparing the arrays. In javascript comparing the array would be a reference comparison, read more about it in this question.
The final result after executing the code is:

Related

Converting 1D array with multiple values into 2D array in google script without Loop in Loop

I'm trying to shorten my script time, as it is reaching the apps script time limit too often (1800s). Therefore, I'm trying to reduce the number of loops the script is performing.
The script goal is to collect all Montecarlo Revenue analysis results, and yo do so it iterates 1000 I (E.g. 1000) times. Each iterations collects the following data points: Total Revenue, # of logos and the same per month.
I've managed to do that through creating a Loop in a Loop (Loopin I for the Montecarlo iterations, and looping J through each data point) and creating a 2D array that later I post in my sheet using SetValues.
for (var I=0; I < Runs; I++) {
MCOutput[I] = new Array(DataLength);
MCOutput[I][0] = I+1;
sheet.getRange(6,18).setValue((I+1)/Runs);
for (var J=1; J<DataLength; J++) {
MCOutput[I][J]=sheet.getRange(5,J+StartCol).getValue();
}
sheet.getRange(Row,StartCol,MCOutput.length,MCOutput[0].length).setValues(MCOutput);
My goal is to reduce the running time, by looping only once and collecting all the data through GetValues. I've managed to do so, but I can't find a way to set these values to a 2D array in the sheet. I'm getting the following error:
Exception: The number of columns in the data does not match the number of columns in the range. The data has 1 but the range has 21.
Here is the script for it:
var MCOutput = [];
for (var I=0; I < Runs; I++) {
MCOutput[I] = new Array(DataLength);
sheet.getRange(6,18).setValue((I+1)/Runs);
sheet.getRange(5,StartCol).setValue(I+1);
MCOutput[I]=sheet.getRange(5,StartCol,1,DataLength).getValues();
}
sheet.getRange(Row,StartCol,I,DataLength).setValues(MCOutput);
I wasn't able to solve it through map or splice, I guess it is because my 1D array has rows and columns of data.
Here are some modification I would suggest.
new Array() can slow down a script and really isn't needed here.
getValues() returns a 2D array so you need to get the first row getValues()[0] of it and push it into the MCOutput array.
sheet.getRange(6,18).setValue((I+1)/Runs); does absolutely nothing that I can see and multiple calls to setValue() can really slow down a script.
you can simple replace the value in MCOutput[I][0] = I+1;
// you are always getting the same row and StartCol or Datalength don't change
let values = sheet.getRange(5,StartCol,1,DataLength).getValues()[0];
let MCOutput = [];
for (let I=0; I < Runs; I++) {
// assuming values is only javascript primitives a shallow copy will do
MCOutput.push(values.map( value => value ));
MCOutput[I][0] = I+1;
}
sheet.getRange(Row,StartCol,I,DataLength).setValues(MCOutput);
Reference:
Best Practices
Array.push()
new Array() vs []

Google Script Overwriting Entire Sheet, Not Specific Range

In the function below, I grab data that is in multiple columns via a form response on my second sheet and place the information on my first sheet organized in rows.
I would like to have the first blank column after the data, currently G on my new sheet editable so that someone can come in and "approve" the contents of each row. Right now, when this script runs, it overwrites the contents of Column G. I thought the number 6 in the line with sh0.getRange(2, 1, aMain.length, 6).setValues(aMain); was telling the script to only put data into 6 columns... looks like that's not the case.
I also thought that I may be able to do a workaround by changing that line to sh0.getRange(2, 2 ... it would let me keep the first column as an editable column... that didn't work either.
Any suggestions to allow me to use this script and keep a column editable?
function SPLIT() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh0 = ss.getSheets()[0], sh1 = ss.getSheets()[1];
// get data from sheet 2
var data = sh1.getDataRange().getValues();
// create array to hold data
var aMain = new Array();
// itterate through data and add to array
// i is the loop, j=3 is the column it starts to loop with, j<9 tells it where to stop.
// in the aMain.push line, use data[i][j] for the rows to search and put in the one column.
for(var i=1, dLen=data.length; i<dLen; i++) {
for(var j=5; j<9; j++) {
aMain.push([data[i][0],data[i][1],data[i][2],data[i][3],data[i][4],data[i][j]]);
}
// add array of data to first sheet
// in the last line, change the last number to equal the number of columns in your final sheet.
// the first number in getrange is the row the data starts on... 1 is column.
sh0.getRange(2, 1, aMain.length, 6).setValues(aMain);
}
}

Move a loop down a column

Is it possible to run a loop and then have it move down 1 column and run again? Basically I liked the loop to run for the values in row 6 and once complete move down to row 7 and run again. I have tried to add a For loop into the macro but I can not get the corresponding rows to match up.
First of all, what language do you want to do this in? Second of all, are you familiar with nested for loops? This is definitely the way to go.
Quick example in JavaScript:
// For each row iterate over its columns
for(var i = 0; i < rows.length; i++) {
var row = rows[i];
for(var x = 0; x < row.columns.length; x++) {
var column = row.columns[x];
}
}

Having trouble understanding multidimensional arrays in as3

Let's say I have some code like:
private function makeGrid():void
{
_grid = new Array();
for(var i:int = 0; i < stage.stageWidth / GRID_SIZE; i++)
{
_grid[i] = new Array();
for(var j:int = 0; j < stage.stageHeight / GRID_SIZE; j++)
{
_grid[i][j] = new Array();
}
}
}
I don't quite understand what's going on. I get that in the first for loop it determines the number of columns needed, and in the second it determines rows, but I don't get why I'm making arrays out of _grid[i] and _grid[i][j].
For instance, _grid[i] = new Array(); get's called 16 times (800px/50px), so that would make 16 arrays right? Why do I need those if the second for loops is already calculating the amount of rows I need?
I'm just going to elaborate on what has already been commented. Let's say that you are creating a 2D grid formed of rows and columns and you wanted to store some sort of data at each "cell" or specified index of the grid.
The first step is to create the first array to hold either the rows or columns (which you choose first doesn't really matter as you can adjust the for loops either way).
The first for loop creates a new row, then in the next inner loop you fill all the columns of that row (if we had chosen columns to be created first then we would fill all the rows of the columns). In this case the inner loop is creating all the columns with another array (making it a 3-dimensional array as mentioned in the comments).
The reason for doing this is for organization and easy look up. If you wanted to see the data stored in the 1st column of the 3rd row it would be as easy as doing _gird[2][0].
Now as to why a 3rd dimension is made as in _grid[i][j] = new Array(); that is specific to what kind of data needs to be stored at that row and column.

How to delete a row after matching a value in an array?

I'm using an array to handle spreadsheet data, and manipulated it. In this case, I am looking for the value of true in a column and then want to delete that row. Here's what I have:
for ( r = 1 ; r < Array1.length; r++) {
if(Array1[r][33]== true) {
calcSheet.deleteRow(Array1[r])};
}
I see the problem, but don't know the solution: firstArray[r] returns the whole row as an array, and not the row number. How can I get the row number? It's eluding me.
UPDATE: Here's the completed code. Thanks again to Serge.
You will notice that Serge pointed out a spreadsheet and array matching problem that would occur. The code below takes care of that as well; that's what all those r's are doing.
var rr = 0
for ( r = 1 ; r < firstArray.length; r++) { // iterate the first col of masterSheet
if(firstArray[r][33]== true) {
var rrr= rr + r
calcSheet.deleteRow(rrr +1);
rr--
}
}
The sheet row corresponding to this array row is r+1 if your array is really complete, ie starting on row1 (I suppose this first row is containing some headers and that's why you iterate from 1 in the array).
But there will be another side effect if you delete the row : the array will not be the exact mirror of the sheet anymore (it will be larger by 1 row each time you delete a row).
there are many ways to workaround this issue, let us know if you want some tips.

Resources