How to export specific columns to text files in Google Sheets - function

I am trying to figure out how can I export the text from a specific column from Google Sheets to a Google Drive .txt file. I am trying to export all the text from all the columns so there would be a different .txt file on google drive for each column. Also would want to skip first 2 rows.
I found this:
function saveToTextfile() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var range = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn());
var rows = range.getValues();
var folder = DriveApp.getFoldersByName("folderName").next();
var files = folder.getFiles();
while(files.hasNext()) files.next().setTrashed(true);
rows.forEach(function(row, index) {
folder.createFile("row" + index + ".txt", row.join(", "));
});
}
This function is doing a text file for all the rows instead of columns and also adds a lot of "," in the resulted txt file. I can't figure it out how to change so it will do the columns, basically I want the function to create a txt file for columns (example: D3 to D100, E3 to E100, F3 to E100 and so on).
Also want this to update the text files when any changes are made on the sheet.
Thanks :)

Transposing the array:
getValues() returns a 2D array where each element in the outer array corresponds to a row. You want to have a 2D array where the outer array elements correspond to columns instead. That is, you need to transpose the 2D array.
An easy way to do that can be found in this answer:
array[0].map((_, colIndex) => array.map(row => row[colIndex]));
Other issues:
If you want to skip the first two rows, the starting row in your range (defined in getRange) should be 3 instead of 1, and correct the number of rows accordingly (check code sample below).
It adds a lot of , because you wrote it that way (see row.join(", ")). If instead you want each value to occupy a new line, use \n instead (check code sample below).
Updating the text files would require you to install an onEdit trigger which would fire the function saveToTextfile every time the spreadsheet is edited. Since this function requires authorization, it cannot be a simple trigger, but an installable one. If you have problems installing it, please post a new question (each question should deal with a specific issue, not with several of them).
Code sample:
function saveToTextfile() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var firstRow = 3; // Skip first two rows => start at 3rd
var range = sheet.getRange(firstRow, 1, sheet.getLastRow() - firstRow + 1, sheet.getLastColumn());
var rows = range.getValues();
var columns = rows[0].map((_, colIndex) => rows.map(row => row[colIndex]));
var folder = DriveApp.getFoldersByName("folderName").next();
var files = folder.getFiles();
while(files.hasNext()) files.next().setTrashed(true);
columns.forEach(function(column, index) {
folder.createFile("column" + index + ".txt", column.join("\n")); // New line
});
}
Reference:
Transposing a 2D-array in JavaScript

Related

How to export each google sheet as its own document using google scripts?

This is my first time using javascript, so maybe I am missing something. The task: I have code that, given a table of information, creates new sheets for each unique name in one of the columns. Notice how Josh chan appears in two of these columns:
With a large enough list, it would be tedious to go through 100 sheets. Is there any way to export each google sheet as its own document?
I've looked hard, I haven't found any way to do this. Any help and expertise would be much appreciated. Side note: this might be possible with macros or something like that.
Thanks!
So if I'm getting this correctly you want the following:
For every unique entry make a new Google Sheets (so a new file, not just a tab)
First things first
You'll have to create a template document which you'll copy every time a new document's made. So for this example, I'll use a sheet called templateSheet as the template, and I'll refer to the main sheet as masterSheet.
Master sheet
Try using this code and filling out the variable templateSheet with the id of your templateSheet
function myFunction() {
// Template sheet stuff
// Insert id of template sheet
const templateSheet = "";
const templateSheetObj = DriveApp.getFileById(`${templateSheet}`);
// Main sheet stuff
const ss = SpreadsheetApp.getActiveSheet();
const allData = ss.getRange(2, 1, ss.getLastRow() - 1, 6).getValues();
let usersDone = [];
for (const entry of allData) {
const fullName = entry[1];
if (!usersDone.includes(fullName)) {
// this makes a copy and sets the title of document to fullname variable
const copy = templateSheetObj.makeCopy(fullName)
usersDone.push(fullName);
}
}
}
Output
How my Drive folder now looks
Content of new document
Since you have the new sheet object saved in copy you can freely do whatever with the new copy, like adding all the rows of data or anything like that.
You need to filter your data with regards to the unique names. You also need to manipulate the data so you can easily loop them all and add them onto a new sheet. There is a lot going on but there are ample comments to state what each step does.
Code:
function createSheetPerUniqueName() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var range = sheet.getDataRange();
var values = range.getValues();
// remove first row as header
var headers = values.shift();
// get unique names
var name = values.map(row => row[1]).filter(function (value, index, self) {
return self.indexOf(value) === index;
});
// traverse unique names
name.forEach(function (uniqueName) {
// get all rows with the unique name
var data = values.filter(row => row[1] == uniqueName);
// pass the id of destination folder (in between "folders/" and "?resourcekey=")
var folder = DriveApp.getFolderById("1UvAE1ZbFso35kiooJ7lUq_nDH2QgUYyS");
// create spreadsheet
var temporarySheet = SpreadsheetApp.create(uniqueName);
// move created spreadsheet to folder and get its ID
var sheetId = DriveApp.getFileById(temporarySheet.getId()).moveTo(folder).getId();
// access the sheet via ID
var newSpreadSheet = SpreadsheetApp.openById(sheetId);
// rename Sheet1 to name
var newSheet = newSpreadSheet.getSheets()[0];
newSheet.setName(uniqueName);
// set header
newSheet.getRange(1, 1, 1, data[0].length).setValues([headers]);
// set data
newSheet.getRange(2, 1, data.length, data[0].length).setValues(data);
});
}
Sample Data:
Output:
Note:
For every unique name, there will be a corresponding spreadsheet to be generated in the chosen folder. And all those rows with that name will be added to that new spreadsheet where it belongs.
Resource:
How to find Folder ID

Script to copy data between sheets

I'm trying to create a script to copy data from sheet 1 to sheet 2 and at the same time reorder it. I get my data from a Google form, so data is constantly updating.
Here are two images as examples. N°1 is how I have my data, N°2 is how I want it to be in sheet 2.
The idea is to have the script copying the data every time a new row appears.
Data from Forms.
This is how I would like it to be.
This is my initial code:
function copyrange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Ingreso'); //source sheet
var testrange = sheet.getRange('J:J');
var testvalue = (testrange.getValues());
var csh = ss.getSheetByName('Auxiliar Ingreso'); //destination sheet
var data = [];
var columnasfijas = [];
var cadena = [];
//Condition check in G:G; If true copy the same row to data array
for (i=1; i<testvalue.length;i++) {
data.push.apply(data,sheet.getRange(i+1,1,1,9).getValues());
if ( testvalue[i] == 'Si') {
data = (sheet.getRange(i+1,1,1,9).getValues()).concat (sheet.getRange(i+1,11,1,9).getValues()); // this beaks up into 2 rows Idon't know why
/*cadena = (columnasfijas);
data.push.apply(data, columnasfijas);*/
}
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
}
//Copy data array to destination sheet
//csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
}
In this line, I'm also having trouble concatenating different lengths of data. It should be: (i+1,1,1,6). concat.....(i+1,11,1,3)
data = (sheet.getRange(i+1,1,1,9).getValues()).concat (sheet.getRange(i+1,11,1,9).getValues()); // this beaks up into 2 rows Idon't know why
When I run it as it should by I receive an error that the length should be 9 instead of 3.
This can be accomplished more simply using formulas instead of app scripts:
=sort(importrange("spreadsheetURL", "Sheet1!A2:AA10000"),sort_col#,TRUE/FALSE,[sort_col2#],[TRUE/FALSE]...)
Documentation on importrange function: https://support.google.com/docs/answer/3093340
Documentation on sort function: https://support.google.com/docs/answer/3093150
Once you input the formula, there will likely red triangle on the cell, be sure to click on the cell and click the Allow Access button to give one spreadsheet access to the other.

Importing CSV into Sheets takes too long

I am trying to import a CSV of approx. 7500 lines from a GMail attachment into Google Sheets and it is taking longer than the allowed 6 minutes to execute.
I am following this tutorial: https://developers.google.com/apps-script/articles/docslist_tutorial#section2 and my code is listed below
The section consuming all the time is the pushing of data into the new sheet:
// Push data into the sheet
for ( var n=0, lenCsv=csvData.length; n<lenCsv; n++ ) {
newsheet.getRange(n+1, 1, 1, csvData[n].length).setValues(new Array(csvData[n]));
}
Is there any way I can improve the performance of this and make it faster?
Thanks in advance.
My code is listed below.
var attachmentData = attachments[k].getDataAsString();
var attachmentClean = attachmentData.split(' ').join(',');
var attachmentCleanA = attachmentClean.split(',');
var csvData = Utilities.parseCsv(attachmentCleanA);
var SheetName = String(csvData[1]).substring(5,7)+ "-" + String(csvData[1]).substring(0,4);
Logger.log("SheetName:"+SheetName);
ss = SpreadsheetApp.openById('XXXXXXXXX');
var sheet = ss.getSheetByName(SheetName);
if (sheet == null){
var newsheet = ss.insertSheet(SheetName);
}else{
sheet.clearContents();
var newsheet = sheet;
}
Logger.log("Entering "+csvData.length+" rows");
// Push data into the sheet
for ( var n=0, lenCsv=csvData.length; n<lenCsv; n++ ) {
newsheet.getRange(n+1, 1, 1, csvData[n].length).setValues(new Array(csvData[n]));
}
This line:
var csvData = Utilities.parseCsv(attachmentCleanA);
Creates a two dimensional array. Google Documentation - parseCsv
If your csvData variable truly is a 2D array, then all you need to do is use this line of code without the for loop:
newsheet.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
The range starts in row one, column one, and sets a range that is the length of the outer array of the csvData, and the number of columns to the number of elements in the first inner array.
Note that if your data has inner arrays of different lengths, setValues() will produce an error.
If all of your inner arrays are not the same length, then you can not avoid looping through every inner array. But, I don't know what your data looks like. If each line of data in your CSV has the same number of vales in the row, then you don't need the for loop.
So, improving the part of the code that is taking all the time, is totally dependent upon whether the rows are all the same length or not.
You shouldn't need the new Array() part in the setValues() method. If the csvData array truly is a good 2D array, then it's already an array. You don't need to create another array from something that's already an array.

Copy a complete list on one spreadsheet to append on the bottom of another spreadsheet

In Google Spreadsheet I would like to take only the values from a complete list on one spreadsheet and append it to the bottom of a list on another spreadsheet. My trouble is that using the the copyValuesToRange() function errors the following:
Target sheet and source range must be on the same spreadsheet.
Here's my current code:
function transferList() {
var source = SpreadsheetApp.getActiveSpreadsheet();
var target = SpreadsheetApp.openById("0ABCD");
var target_sheet = target.getSheetByName("RFPData");
var sheet = source.getSheetByName("RFP List");
var sheet_last_row = sheet.getLastRow() + 1;
var source_range = sheet.getRange("A2:I"+sheet_last_row);
var sWidth=source_range.getWidth() + 1;
var sHeight=source_range.getHeight() + 1;
var last_row=target_sheet.getLastRow();
source_range.copyValuesToRange(target_sheet , 1, sWidth,
last_row + 1, last_row + sHeight );
}
Any idea how I can get this to work?
As you've found, copyValuesToRange() is a Range method that affects a Sheet Object that is in the same Spreadsheet Object as the Range. There isn't an atomic method that will copy a range of values to another Spreadsheet, but there are a number of ways you could do it.
Here's one way.
From the source sheet, get all the data in one operation, by selecting the complete range of data using getDataRange() and then grabbing all values into a javascript array with getValues().
To ignore the first row of headers, use the javascript array method splice().
Locate your destination, which is on the target sheet, starting after the last row of data that's currently there, using getLastRow().
Write the source data (without the headers) to the destination sheet starting at the next row, using setValues().
Script:
function transferList() {
var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("RFP List");
var sourceData = sourceSheet.getDataRange().getValues();
sourceData.splice(0,1); // Remove header
var targetSS = SpreadsheetApp.openById("0ABCD").getSheetByName("RFPData");
var targetRangeTop = targetSS.getLastRow(); // Get # rows currently in target
targetSS.getRange(targetRangeTop+1,1,sourceData.length,sourceData[0].length).setValues(sourceData);
}
For some historic dashboard I created by importing and appending data, I use an add-on called Sheetgo. Save me programing time and help me with traceability issues.

I need to copy data from 3 sheets to another master sheet in same spreadsheet

I have written a code for this but getting an error
The coordinates or dimensions of the range are invalid. (line 44)
Code:
function updateMaster() {
var repArray = new Array();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var allSheets = ss.getSheets();
// build array of all sheets
for (i in allSheets) {
if ((allSheets[i].getName()).match(/.*?\-Rep$/))
{repArray.push(allSheets[i].getName());}
}
// store all sheets in array
var sheetArray = [];
// loop through all rep sheets
for (var j in repArray) {
// get each sheet
var tempSheet = ss.getSheetByName(repArray[j]);
// get sheet data
var dataRange = tempSheet.getDataRange().getValues();
// remove the first header row
dataRange.splice(parseInt(0), 1);
// append sheet data to array
var sheetArray = sheetArray.concat(dataRange);
}
// Time to update the master sheet
var mSheet = ss.getSheetByName("summary");
// save top header row
var headerRow = mSheet.getRange(1,1,1,12).getValues();
// clear the whole sheet
mSheet.clear({contentsOnly:true});
// put back the header row
mSheet.getRange(1, 1, 1, 12).setValues(headerRow);
This is where i am getting error while writing to master sheet:
// write to the Master sheet via the array
mSheet.getRange(2, 1, sheetArray.length, 12).setValues(sheetArray);
// force spreadsheet updates
SpreadsheetApp.flush();
// pause (1,000 milliseconds = 1 second)
Utilities.sleep("200");
// delete empty rows at bottom
var last = mSheet.getLastRow();
var max = mSheet.getMaxRows();
if (last !== max) {mSheet.deleteRows(last+1,max-last);}
}
I am not able to figure out the error.
You need a sheet with "Rep" inside the name.
"The first array stores all the Sales Rep sheets. Since some sheets could be something other than Sales Rep sheets, the script stores sheets only if the sheet name has a “-Rep” suffix (e.g. “JohnDoe-Rep”)"
code source: http://blog.ditoweb.com/2012/01/consolidate-spreadsheet-sheets-with.html
That's why it's not working.
mSheet.getRange(2, 1, sheetArray.length, 12).setValues(sheetArray)
I faced the same issue and solved it by following the "Rep" solution. Then I ran into another problem and realized that my number of columns was different... so just go back in and change the no 12 to the number of rows you have in your sheet.
Also try to keep the format of the all sheets the same.
Rename the sheet where you would like the data to combine