Cut and paste data range to bottom of another sheet - javascript

I'm new to google app script and not sure how to solve this issue.
I have two sheets, "Main" and "Extract". Throughout the day, data is pasted into the "Extract" sheet, cleaned up (script removes certain columns and removes rows where a value in column A also exist in column A of the "Main" sheet), and then added to the "Main" sheet, which contains all of the cleaned up extracts pulled throughout the day.
I am trying to write a script that will take all of the cleaned up data in the "Extract" sheet and cut and paste it beneath the last row with data in the "Main" sheet.
Once cleaned up the "Extract" sheet always has 8 columns (A-H), but the amount of rows varys each time. I do not need the header or column 8 (H) pasted to the "Main" sheet, only A2:G (stopping at the last row with data).
So far this is all I have been able to come up with (it is likely very wrong)
var data = sheet.getDataRange().getValues()
.offset(0, 0, 1);

I believe this is what you are trying to do.
var spread = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spread.getSheetByName("Extract");
// skip row 1 (header) and beyond column G
var range = sheet.getRange(2,1,sheet.getLastRow()-1,7);
sheet = spread.getSheetByName("Main");
var rows = sheet.getRange(1,1,sheet.getLastRow(),1).getValues();
// search for first blank row column 1
for( var i=0; i<rows.length; i++ ) {
if( rows[i][0] === "" ) {
var offset = sheet.getRange(1,1).offset(i,0);
range.copyTo(offset);
break;
}
}

Related

Automating named Range function in google sheets

Context
So I have a spreadsheet with 11+ sheets, though will be adding more later. I want to dynamically name the columns using named range. I created a macro script and have been using this. Only the problem is for every sheet, I have to go to the script and change the name of the named range:
function NamedRanges() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.setNamedRange('TrainlineDate', spreadsheet.getRange('A:A'));
spreadsheet.setNamedRange('TrainlinePrice', spreadsheet.getRange('B:B'));
spreadsheet.setNamedRange('TrainlineReturns', spreadsheet.getRange('C:C'));
spreadsheet.setNamedRange('TrainlineGrossReturns', spreadsheet.getRange('D:D'));
spreadsheet.setNamedRange('TrainlineGeometricReturns', spreadsheet.getRange('E:E'));
spreadsheet.setNamedRange('TrainlineRisk', spreadsheet.getRange('F:F'));
spreadsheet.setNamedRange('TrainlineNegativeReturns', spreadsheet.getRange('G:G'));
spreadsheet.setNamedRange('TrainlinePositiveReturns', spreadsheet.getRange('H:H'));
spreadsheet.setNamedRange('TrainlineTimeValueMoney', spreadsheet.getRange('I:I'));
};
For example, for the next tab, I have to change Trainline to Softcat.
I tried my hand at creating the columns as an array and then a for loop to create a named range according to the name in cell B2 + the name in the headers (the 3rd row of every column). I will also be adding new columns in the future. But when I tried to log the value in cell B2 it failed. Long story short, I am stuck. This is what I came up with thus far;
function NamedRange() {
const ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var col = ss.getLastColumn(); //Get all the columns in the sheet with data (there will be no gaps/spaces)
var stockName = ss.getRange("B1"); // name in cell B1 (column 2, row 1)
var headerCell = ss.getRange(3, col); // in row 3 of all columns
var rangeName = ss.getValue(stockName);
Logger.log(rangeName) // test to see if the value of stockname is picked up by Appscript
// For every column, get the values in 'stockName and headerCell' and create a named range from them.
An exception is for the second column, instead of 'Close' name, call it 'Stockname' + 'Price'.
};
Here is an example of the spreadsheet format. This script though will be applicable to Aveva Group tab onwards.
Problem
Typically the format of the spreadsheets will all be the same. The only difference will be the name of the stock. I want to be able to automatically set the namedRange of all columns for existing and new columns for every tab/sheet and trigger it using a custom menu (this part I can sort out myself). Is there a better way to get this done?
Explanation:
Create an array of all the sheet names you would like to be part of this process.
ForEach sheet name, set the name ranges according to that name by concatenating the sheet name with the namerange name.
Solution:
function NamedRanges() {
var spreadsheet = SpreadsheetApp.getActive();
//put all the sheets here you want to include
var sheetNames = ["Trainline","Softcat","Avast"];
var namerng=['Date','Price','Returns','GrossReturns','GeometricReturns',
'Risk','NegativeReturns','PositiveReturns','TimeValueMoney'];
sheetNames.forEach(sh=>{
sheet = spreadsheet.getSheetByName(sh);
namerng.forEach((nr,i)=>{
spreadsheet.setNamedRange(sh+nr, sheet.getRange(1,i+1,sheet.getMaxRows(),1));
});
});
};
Save also this code to your script editor and it will create a menu button at the top menu of the spreadsheet file that will execute NamedRanges:
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Custom Menu')
.addItem('Name Ranges', 'NamedRanges')
.addToUi();
}

Copy filtered range from one spreadsheet to another - Google app script

I have a large google sheet with 30275 rows and 133 columns in a google sheet. I want to filter the data and copy column AZ to another spreadsheet.
Link to spreadsheet: https://docs.google.com/spreadsheets/d/1aiuHIYzlCM7zO_5oZ0aOCKDwPo06syXhWvhQMKgJE2I/edit?usp=sharing
I have been trying to follow this link
I am not that familiar with javascript and the code is designed to exclude items from filter rather than including items on filter. I have 500+ items to exclude so need to work out something that will be efficient in filtering large dataset in short time before execution limit is reached.
Here is my code so far. Any help to get this working would be appreciated.
NOTE: Filter/ Query with importrange formulas dont work due to the large volume of data. So I need an efficient script to filter large dataset and move them to another sheet before execution time limit.
function filtered() {
var ss = SpreadsheetApp.openById('1u9z_8J-tvTZaW4adO6kCk7bkWeB0pwPcZQdjBazpExI');
var sheet = ss.getSheetByName('Sheet1');
var destsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('JockeyList');
var demosheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Betting data - Demo');
var jockey = demosheet.getRange('L14').getValues();
// Get full (non-filtered) data
var values = sheet.getRange('A:EC').getValues();
// Apply filter criteria here
//Logger.log(jockey);
var hiddenValues = jockey;
values = values.filter(function(v) {
return hiddenValues.indexOf(v[51]) == 1;
});
Logger.log(values.length);
// Set filtered data on the target sheet
destsheet.getRange(10, 5, values.length, 2).setValues(values);
}
Ok so it seems like you want to copy only the values from AZ in 'Sheet1' that are equal to whatever string value is contained in cell L14 of sheet 'Betting data - Demo.' If that is the case, then here is a change to your original code that will accomplish that:
function filtered() {
var ss = SpreadsheetApp.openById('1u9z_8J-tvTZaW4adO6kCk7bkWeB0pwPcZQdjBazpExI');
var sheet = ss.getSheetByName('Sheet1');
// this assumes that your destsheet and demosheet are in the same spreadsheet. Keep in mind that opening spreadsheets with SpreadsheetApp is costly so you want to minimize your calls to new sheets.
var destsheet = ss.getSheetByName('JockeyList');
var demosheet = ss.getSheetByName('Betting data - Demo');
var jockey = demosheet.getRange('L14').getValue();
var searchTerm = new RegExp(jockey);
// Get full (non-filtered) data
var values = sheet.getRange('A:EC').getValues();
// Apply filter criteria here and return ONLY THE VALUE IN COLUMN AZ
var filteredValues = values.reduce(function(resultArray, row) {
if (searchTerm.test(row[51])) resultArray.push([row[51]]);
return resultArray;
}, [])
// Set filtered data on the target sheet
// Note* not clear why you are starting at row 10, but as is this will overwrite all of the data in column 5 of destsheet starting from row 10 every time this function runs
destsheet.getRange(10, 5, filteredValues.length, 1).setValues(filteredValues);
}
As it says in the code sample, this will only copy and paste the value in column AZ of 'sheet1'. It does not alter 'sheet1' in any way. If that is really all you want to do, then this function works, but it's overkill. Since you are just filtering values in AZ against a single string value, it would be more efficient to simply count the number of times that string occurs in column AZ and then add the string that number of times to your destination sheet.
Also note that your original function is pasting values into destsheet into a constant row (row 10). That means that every time your function runs, the data from row 10 on will be overwritten by whatever data you are copying from 'sheet1'

I need help creating a script to hide columns in google sheets if cells in range are empty

I am trying to create a script which will hide columns where cells in a range are empty (the condition).
Below is what my table looks like, you can see there are 12 roles. I basically want to hide any columns from Role 4 onwards which are unused e.g. no values in the 3 rows.
i have a script that works (see below) but it also hides Roles 1, 2 and 3 if they are blank. I want the script to only work from Role 4 (column F) onwards. What do i need to change in the script for this to work?
P.s. I have used code from other posts to put this together so there may be lines in the code I do not need, I am still very new to google script.
function hidecolumns() {
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getSheetByName("Project Team Resources");
var r = ss.getRange("C5:N10");
var data = r.getValues();
var rData, cData, x;
cData = data[0].map(function (col, c) {
return data.map(function (row, r) {
return data[r][c];
});
});
Logger.log(cData.length);
Logger.log(data.length);
for(var i=0;i<cData.length;i++){
if(cData[i].filter(String).length==0)
ss.hideColumns(i+3)
}
}
Explanation:
Your goal is to hide a column if it is empty with the exception of the first three columns in the range.
One approach to achieve this to iterate over all columns in the desired range and check if they contain at least one non-empty value (length>0). If the length is 0, meaning that all values in this column are empty, then hide it.
In more detail, I use a forEach loop to iterate over each column. Each column is given by a simple map expression:
data.map(d => d[col]) where col takes values 0, 1, 2, ...
I filter on the non-empty values and hide only columns if the length of the non-empty array is 0.
Since the data range starts from column C which is the 3rd column, I hide column col+3. Be careful with this, since it depends on the input range.
Finally, your goal is to keep the first 3 columns (Roles 1,2,3) fixed-unhidden regardless if they contain values or not. To do that, add an extra condition in the if condition to check if column is larger than 2: col>2.
Solution:
function hidecolumns() {
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getSheetByName("Project Team Resources");
var data = ss.getRange("C5:N10").getValues();
data[0].forEach((_,col)=>{
if(data.map(d => d[col]).filter(e=>e!='').length==0 && col>2){
ss.hideColumns(col+3);
}
});
}
underscore as a parameter reference

getRange() with specific criteria in Google Apps Sheets Script

I'm very new to JavaScript, and have only recently started fiddling with coding.
I had found a question that is similar to what I'm asking, so I've tried to modify the suggestions to fit my needs, but am drawing a blank pulling together other resources to understand the solution.
That question and answer is here: range.getValues() With specific Date in Specific Cell
I hope to ask my question by adding it to that thread, but I'm unable to make a comment - only an Answer, as I have zero votes.
I want to know how I can use the range.setValues() function, but specify only rows of cells that fit a certain criteria (e.g. cell in column B = 1).
Mostly I understand the answer, I think - push the full list of data into another array, then use a for loop to identify which rows to "keep".
However, I don't understand a good chunk of the solution, specifically this portion:
data.forEach(function(row) {
var colHvalue = row[7];
var colKvalue = row[10];
if( /* your desired test */) {
// Add the name of the sheet as the row's 1st column value.
row.unshift(sheetName);
// Keep this row
kept.push(row);
}
});
where I'm confused:
What's function(row) refer to? It seems to me that "row" is not a defined variable, and is also not a method.
What are the objects being specified in the line var colHvalue = row[7];? And same for the second row.
How does the pushing and unshifting work?
Hope for some answers, thanks!
Edit:
So I still don't understand the detailed logic of the Answer to that other question, and wrote something else based off that:
function importtest() {
var sheetX = SpreadsheetApp.openById("XXX-XXX-XXX"); // workbook containing orginal data
var tabX = sheetX.getSheetByName("EXPORTER"); // tab containing original data
var rangeX = tabX.getRange(6,2,sheetX.getLastRow(),sheetX.getLastColumn()).getValues(); //range of full original data
var kept = [] ;
var data = rangeX ; //storing full data in "data"
// define function "latestrows", which fills up the "kept" array with selected rows
function latestrows() {
var datesent = data[i][2] ; // indicating dynamic cell position (using variable i) that determines if should be kept or not
var datarow = data[i] ; // specify the entire row of data
// loop statement; starting with i=0 (or row 1), and increasing until the last row of "data"
for ( i = 0; i < data.length ; i++) {
if (datesent == 1) { // if cell in the col indicating if it should be kept, is "1"
kept.push(datarow); //move the chosen rows of data into "kept"
}
}
}
var sheetX3 = SpreadsheetApp.openById("XXX-XX-XX"); // workbook import destination
var tabX3 = sheetX3.getSheetByName("IMPORTED"); // import destination tab
var rangeX3 = tabX3.getRange(7,3,kept.length,kept[0].length) // imported array range
rangeX3.setValues(kept); // set values
}
I get the error message TypeError: Cannot read property "length" from undefined. (line 31, file "Code"), which together with testing other modifications indicates to me that kept is empty. What am I missing?

How do I get the value in a Google Sheets cell and test its contents against a range of cells?

I'm trying to speed up my workflow. Typically I enter the CPU of a computer we are selling and then look up its speed and core count etc. But I want to have this information auto-fill as I record the CPU (based on information I have already put into another sheet).
How can I get the value of a particular cell so I can test it against my pre-recorded CPU's?
In Python I'm looking for something like this..
Spreadsheet = SpreadsheetApp.getActiveSheet()
if Spreadsheet.currentSheet == 'sheet1':
active_cell = Spreadsheet.GetActiveCell()
if active_cell.value != None:
cpu = cell.value
row = active_cell.getRow()
if cpu in Spreadsheet.Sheet2.cellRange(B2:B1000):
row2 = row matching cpu
Spreadsheet.sheet1.cell( E, row).value = Spreadsheet.Sheet2.cell( C, row2 ).value
Spreadsheet.sheet1.cell( F, row).value = Spreadsheet.Sheet2.cell( D, row2 ).value
This is what I have currently..
function onEdit() {
var s = SpreadsheetApp.getActiveSheet(); // current Spreadsheet Document?
if( s.getName() == "Sheet1" ) {
var active_cell = s.getActiveCell();
if( active_cell != null ) {
}
}
}
I believe the above code is checking if the cell itself doesn't exist. How do I proceed to get the contents of the cell?
How can I test that value against the range of cells?
Also, is the line var s = SpreadsheetApp.getActiveSheet() referencing the entire document or sheet1?
First, SpreadsheetApp.getActiveSheet() return the active Sheet. If you want the Spreadhseet you should use SpreadsheetApp.getActive().
Then, in order to access the content of a cell, you must first specify its Range. For instance, if you want all the cells of the sheet you can do : SpreadsheetApp.getActiveSheet().getDataRange().getValues(), it will return an array of rows (each row contains as cells as columns in your sheet). If you want a specific cell you can do it via the getRange(...) methods (documentation here).
EDIT : Adding active cell details
And if you want to access the active cell value, SpreadsheetApp.getActiveSheet().getActiveCell().getValue() would suffice.

Categories

Resources