Get data from a lot of different sheets with a script in Google Spreadsheet - loops

I have a spreadsheet containing 50 sheets, named "1", "2", "3". The very first sheet is a summary, gathering specific information from the different sheets. The simple way to fill it in is by using ='1'!B5 and so on. What I want to do is enter that formula into a script and have it pull the specified data from all the different sheets on new rows. So that info from sheet 1 is at row 1, info from sheet 2 is on row 2.
The reason I don't want to do it manually is that I have 14 cells that I need to pull and insert into 4 different overviews from 50 sheets and 2 spreadsheets where I need to do this. Adding up to 5600 functions that I would need to change manually.
The reason I named the sheets in this simple manner is that I know it enables me to write this kind of function. A variable that increases its value by 1 for every loop and outputs the function on a new row with every loop, for instance
var sheetName = 0;
output.setValue('='' + sheetName + ''!B5');
I know I should have a variable for the specified output cell that also increase its value by 1 for every loop. But I have no idea how to get this sorted out, my scripting skills only allow me to modify others script to make them suit my own purposes but not write anything by myself and I have been unable to figure this one out more then the theory behind it. I know this isn't the place to ask for ready made code so I don't demand or even expect a solution, I just felt I had to try and ask for aid.
Here is a dummy which shows what I'm trying to do.
https://docs.google.com/spreadsheet/ccc?key=0ArjDeqGD779cdEh2Ynh5ODBFQVFkVE9lX0p4aWpXelE&usp=sharing#gid=0

You are a lucky man Mattis! I was doing this thing myself earlier this week so I have added a full working script to your dummy sheet. I'll add a copy here for completeness. Any questions please let me know :)
function populateOverview() {
//set basics
var ss = SpreadsheetApp.getActiveSpreadsheet();
var viewSheet = ss.getSheetByName('Overview');
var dataLength = 4; //this is how many cells from each sheet
//create array of sheet names and remove 'overview'
var sheetNames = getSheetNames();
var overviewIndex = sheetNames.indexOf('Overview');
sheetNames.splice(overviewIndex, 1);
//set initial data and target columns
var dataColumn = 2;
var dataRow = 2;
var targetColumn = 3;
var j = 0;
//outer loop iterates through sheets
for (var i = 0; i < sheetNames.length; i++){
//inner loop works across the columns in each sheet, and across the column on the overview
for(var k = 0; k < dataLength; k++){
viewSheet.getRange(j+3, targetColumn+k).setValue(ss.getSheetByName(sheetNames[i]).getRange(dataRow, dataColumn).getValue());
dataColumn = dataColumn + 2;
}
//increments row counter for next sheet input
j = j+1;
//resets for next sheet
dataColumn = 2;
targetColumn = 3;
}
}
/*return sheetnames as array*/
function getSheetNames(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetArray = ss.getSheets();
var sheetNameArray = [];
for(var i = 0; i<sheetArray.length; i++){
sheetNameArray.push(sheetArray[i].getSheetName());
}
return sheetNameArray;
}

Related

Apps Script: Move SELECTED rows from one sheet to the last available rows of another sheet

The code below is from here and was originally written by user #Cooper. All it does is that it copies the values of the columns specified in the array colA = [1,3,2,4] from SHEET 1 and add them to SHEET 2. I want to re-write the 2 last lines of the code, so that the values coming from SHEET 1 are added to the last available rows in SHEET 2. By "2 last lines", I mean these last lines of the code:
drng = des.getRange(1,1,drngA.length,drngA[0].length);//destination array controls size of destination range.drng.setValues(drngA);
Any idea how to get this done?
Thank you so much in advance for your help!
function Copy_data()
{
var src = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SHEET 1");
var des = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SHEET 2");
var srng = src.getDataRange();
var srngA = srng.getValues();
var drngA = [];
var colA = [1,3,2,4];//indirect data column selection
for (var i = 0; i < srngA.length;i++ )
{
var k = 0;
drngA[i]=[];
for(var j=0;j<srngA[i].length;j++)
{
drngA[i][k++] = srngA[i][colA[j]-1];
}
drng = des.getRange(1,1,drngA.length,drngA[0].length);//destination array controls size of destination range.
drng.setValues(drngA);
}
}
You can get the last row of the destination Sheet and after, add it to your getRange() function. The method proposed by #soMario will not work because it makes requests inside the loop while you update the Sheet, and this causes it to get different values every time you call des.getLastRow().
The simplest solution is to take the getLastRow() request outside of the loop and then include it in getRange. Note that one is added to it so that it does not overwrite the last row with the setValue() request.
Code.gs
function Copy_data() {
...
var lastRow = des.getLastRow() + 1
for (var i = 0; i < srngA.length;i++ ){
...
drng = des.getRange(lastRow,1,drngA.length,drngA[0].length)
...
}
Documentation
getRange(row, column, numRows, numColumns)
getLastRow()

Is there a way to highlight duplicate combinations of values in two columns, regardless of the order?

I have a UFC database and I'm looking for rematches. Therefore, I need to find the duplicate combinations of names; the combinations that appear more than once. However, since the winner of the first fight could lose the rematch, I need to find duplicates regardless of the order in which they appear.
This is how my database is structured:
database example.
Fighter 1 is the winner and fighter 2 is the loser.
Here is a link to the database (got it from kaggle): https://docs.google.com/spreadsheets/d/19ISNhYFdGzgLZz1x4h2v_Q5Pq0cofw2rkUtwk3xPXGQ/edit?usp=sharing
Feel free to play around with it.
Any ideas on how to solve this?
Here is an example of the result I'd ideally want:
image example
Just a simple highlight over the duplicates.
In order to give you more precise help, it would be helpful if you could give an example of the result you want. As has been said, if there are many duplicates and you mark each group with a different colour, it can be visually confusing.
Anyway, I have written a couple of functions with Apps Script and Spreadsheet Service that you may find useful for your project.
Code 1
With these functions, you will be able to get all fights where two fighters that you previously define have participated:
function main() {
var name1 = 'Conor McGregor'
var name2 = 'Nate Diaz'
var result = findDuplicates(name1, name2)
console.log(result)
}
function findDuplicates(name1, name2) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Ark1')
var lastRow = sheet.getLastRow()
var lastCol = sheet.getLastColumn()
var range = sheet.getRange(1, 1, lastRow, lastCol).getValues()
var result = []
for (var i = 0; i < range.length; i++) {
if (range[i].includes(name1) && range[i].includes(name2)) {
result.push(range[i])
}
}
return result
}
Code 2
With this function, you can add in a third column, the same number for each pair of fighters. This way, it is very easy to change the "add a number" to "paint the cells in a colour".
function findPairs() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Ark2')
var lastRow = sheet.getLastRow()
var lastCol = sheet.getLastColumn()
var range = sheet.getRange(1, 1, lastRow, lastCol).getValues()
var indexToSkip = []
var k = 0
for (var i = 0; i < range.length; i++) {
if (!indexToSkip.includes(i)) {
var match = range[i]
indexToSkip.push(i)
sheet.getRange(i + 1, 3).setValue(k)
for (var j = i + 1; j < range.length; j++) {
if (range[j].includes(match[0]) && range[j].includes(match[1])) {
indexToSkip.push(j)
sheet.getRange(j + 1, 3).setValue(k)
}
}
k = k+1
}
}
}
References:
Apps Script
Spreadsheet Service

Pulling scattered data from changing spreadsheet

I'm trying to write a script which has multiple parts. The current part (function copyOver) supposed to open a spreadsheet (by ID), extract specific data from it and insert it into a Master spreadsheet.
The part I'm stuck with is that the spreadsheet changes daily. Some days there are cells containing data of "Sea Water", other days there aren't any. Ideally, I was trying to write a script which loops through the sheet looking for the specific tab names in cells (for example: Sea water, chlorine content...) and extract the data from the row below, up until the second tab and so on. I would only need very specific data, like data from "C6", "E6", "F10", but these cells are always changing so I have to look for them by using the tab names in cells above them. There would be multiple arrays for each tab and the data coming with it.
Would that be possible to extract data this way and put them into an array containing the tab value as the header or title and the data connected to that specific tab.
var sourceID = "source sheet ID";
var main = SpreadsheetApp.openById("master sheet ID"); // MASTER
var source = SpreadsheetApp.openById(sourceID); //the workbook you're copying from
var mainsheet = main.getSheetByName("Lab Data"); // the sheet you want to copy the stuff into
var sourcesheet = source.getSheetByName("ANALYSIS REPORT"); // the sheet you're copying from
var dataRange = sourcesheet.getDataRange().getValues(); // gets sheet as data range
// finds SEA WATER tab in cell and gets the range below until the end of the document
// this is the range I need to find the rest of the tabs and the data under them.
var cont = "SEA WATER";
var i = [];
for (var y = 0; y < dataValues.length; y++){
if(dataValues[y] == cont){
i.push(y);
} // end if
} // end for
Logger.log(i);
var Row = Number(i)+Number(dataRange.getRow()); // gets the row number of SEA WATER tab
Logger.log("row number: " + Row);
var swLast = sourcesheet.getLastRow();
var swRange = sourcesheet.getRange(Row,2,swLast,11);
var swValues = swRange.getValues(); // range of needed information
Logger.log("sw: " + swValues);
var con2 = "SW outlet SW from Coarse filtration"; // looking for the secondary tab, within the
range
I got from the first loop
var res2 = [];
for(var i2 = 0; i2 < swValues.length; i2++) {
if(swValues[i2][4] === con2) res2.push(data[i2])
var row2 = Number(i2)+Number(swRange.getRow());
Logger.log("row2 " + row2);
} // for end
var look1 = "SW outlet SW from Coarse filtration ";
for(var i1 = 0; i1<dataRange.length;i1++){
if(dataRange[i1][1] == look1){
return i1+1;
}
}
Logger.log((i1+1));
} // end of function
EDIT: Here's a link to a sheet. Required data starts from row 237, until row 268 - but this can change every day. (Deleted the information for privacy reasons.)
Basically, I need all the cells with "x" in them, preferably together with the title cells above them, so I'll know what data it is.
The following code loops through your headers in column B and finds the row in which your header of interest is located
Subsequently, it copies the data starting from this row to the last data row into the sheet "Lab Data":
function myFunction() {
var sourceID = "source sheet ID";
var main = SpreadsheetApp.openById("sourceID"); // MASTER
var source = SpreadsheetApp.openById(sourceID); //the workbook you're copying from
var mainsheet = main.getSheetByName("Lab Data"); // the sheet you want to copy the stuff into
var sourcesheet = source.getSheetByName("ANALYSIS REPORT"); // the sheet you're copying from
var dataRange = sourcesheet.getDataRange()
var dataValues=dataRange.getValues(); // gets sheet as data range
for (var a = 0; a < dataValues.length; a++){
if(dataValues[a][1]=="SEA WATER"){
var Row=a+1;
break;
}
}
if(Row){
var swLast = sourcesheet.getLastRow();
var swRange = sourcesheet.getRange(Row,2,swLast,11);
swRange.copyTo(mainsheet.getRange(mainsheet.getLastRow()+1, 2));
}
} // end of function
I hope this helps to solve your issue.

Build the proper array with push method to send data to google sheet in the right format

For a very specific goal, for which I won't go into detail here, I need to grab some data from one spreadsheet and send these data to another one. However I must use the push method somewhere in my code.
Concretely here is what I'm trying to do :
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Raw_data");
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet10");
var Row_count = sheet.getLastRow()
var raw_data = sheet.getDataRange().getValues();
var data = [];
for (var i = 0; i < Row_count ; i++) {
data.push(raw_data[i].toString())
}
sheet2.getRange(1, 1,1, 307).setValues([data]);
}
So basically I would like to be able to breakdown the getValues(); output and recompose it with the push method.
Right now all my data are being send in sheet2 on the same row. Using the push method how do I go to get the following output :
Header1,Header2,Header3,Header4,etc...
Data11,data12,data13,data14
Data21,data22,data23,data24
Data31,data32,data33,data34,
etc...
thanks !
According to the documentation, the range you're pushing that data into should be the same shape as the data you're pushing, but it isn't if I'm reading getRange correctly. There are two differences: Your range is only one row high, and your range is 307 columns wide. But your data is Row_count rows high, and one column wide (with that column containing the result of converting the cells into a single comma-delimited string).
So I'd think changing your getRange at the end would be sufficient:
sheet2.getRange(1, 1, Row_count, 1).setValues([data]);
// -------------------^^^^^^^^---^
In a comment, you've said you want the data you pass to setValues to be the same shape as raw_data. Right now it isn't, raw_data is a two-dimensional structure; you're building a one-dimensional array with string-joined values and then wrapping it in another array — so it ends up being two-dimensional, but it's one row with Row_count columns.
You'll need nested loops if you want to build the same structure:
var data = [];
var row;
var dataRow;
var rowIndex;
var Col_count = raw_data[0].length;
var colIndex;
for (rowIndex = 0; rowIndex < Row_count; rowIndex++) {
row = raw_data[i];
dataRow = [];
data.push(dataRow);
for (colIndex = 0; colIndex < colCount; colIndex++) {
dataRow.push(row[colIndex]);
}
}
sheet2.getRange(1, 1, Row_count, Col_count).setValues(raw_data);

My Apps Script loop is not looking at more rows than the first

I have a general spreadsheet in which Column B has a number 1-9. I want to copy each row item to the corresponding sheet I have created, sheet 1-9. However, I set my loop to begin at row 5 and continue the length of all rows in the data range but the loop does not continue past row 5 even though more rows have a "1" in column B. Can anyone help to see why my loop is not continuing? Thanks!
function GoodFunction() {
var spreadsheet = SpreadsheetApp.getActive()
var R0sheet = spreadsheet.getSheetByName('S14Allocation')
var R1sheet = spreadsheet.getSheetByName('R1-Allocations')
var ss = spreadsheet;
var s = R0sheet
var targetSheet = R1sheet
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
var r = s.getDataRange();
var numColumns = s.getLastColumn();
var numRows=s.getLastRow();
for (var i = 5; i <= numRows.length ; i++); {
if (s.getDataRange().getCell(i,2).getValue() === 1) {
s.getRange(i,1,1,numColumns).copyTo(target);
}
}
}
numRows is already a number (from s.getLastRow();) so use i < numRows as the condition in your for loop.
As Serge said in his comment though, the code is very confusing. There's no need for that many variables, and there are more efficient ways of dealing with multiple cells at once.
https://developers.google.com/apps-script/best_practices#batchOperations

Resources