Google spreadsheet: Download all the sheets at once - google-sheets

When working with google spreadsheet, how to download all the sheets at once?
I want to use the option:
Comma-separated values
But it only download the current sheet, how to get them all?

For anyone who navigates to this question, trying to download all the tabs in their Google spreadsheets as CSV files at once, even in 2021, there does not seem to be a GUI button to do this. At least I could not see anything. The answer by #Amit Agarwal does well, to get all sheets, but if your file has comma-delimited data in cells, then data could get mangled.
I took Amit's approach https://stackoverflow.com/a/28711961 and combined it with Michael Derazon and Aaron Davis's approach here https://gist.github.com/mrkrndvs/a2c8ff518b16e9188338cb809e06ccf1 to dump all the tabs of a chosen Google spreadsheet into a folder in Google Drive. You can then just download the folder with a single click.
The following is Google script, not exactly a Javascript, and you would have to copy-paste this in https://script.google.com/ login with your Google id, and then create a project and then create a script app, and save and execute this.
// https://stackoverflow.com/a/28711961
function export_sheets_as_csv_to_folder() {
// Sheet id is in URL https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID/edit#gid=IGNORE
var ss = SpreadsheetApp.openById('YOUR_SHEET_ID');
var sheets = ss.getSheets();
if (sheets === undefined || sheets.length === 0) {
return;
}
var passThroughFolder = DriveApp.createFolder('YOUR_PREFERRED_FOLDER_NAME_IN_DRIVE');
for (var s in sheets) {
var csv = convertRangeToCsvFile_(sheets[s])
passThroughFolder.createFile(sheets[s].getName() + ".csv", csv);
}
}
// https://gist.github.com/mrkrndvs/a2c8ff518b16e9188338cb809e06ccf1
function convertRangeToCsvFile_(sheet) {
// get available data range in the spreadsheet
var activeRange = sheet.getDataRange();
try {
var data = activeRange.getValues();
var csvFile = undefined;
// loop through the data in the range and build a string with the csv data
if (data.length > 1) {
var csv = "";
for (var row = 0; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
if (data[row][col].toString().indexOf(",") != -1) {
data[row][col] = "\"" + data[row][col] + "\"";
}
}
// join each row's columns
// add a carriage return to end of each row, except for the last one
if (row < data.length-1) {
csv += data[row].join(",") + "\r\n";
}
else {
csv += data[row];
}
}
csvFile = csv;
}
return csvFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}

After clicking download > pdf, select export > worksheet (instead of current sheet which is the default)

You can use Google Scripts to save all the sheets of a spreadsheet into separate files.
function myFunction() {
var ss = SpreadsheetApp.openById(SHEET_ID);
var sheets = ss.getSheets();
for (var s in sheets) {
var csv = "";
var data = sheets[s].getDataRange().getValues();
for (d in data) {
csv += data[d].join(",") + "\n";
}
DriveApp.createFile(sheets[s].getName() + ".csv", csv);
}
}

the answer from #Soham works amazingly but it doesn't handle multiline values. It would be an easy fix just to add more checks to character \n along with , but I took the liberty to rewrite the function using map (and string.includes) so it is more concise.
function convertRangeToCsvFile_(sheet) {
return sheet.getDataRange().getValues()
.map(row => row.map(value => value.toString())
.map(value => (value.includes("\n") || value.includes(",")) ? "\"" + value + "\"" : value)
.join(','))
.join('\n')
}

A slight variation on this that uses a zip instead of a folder to contain the sheets and does some modernizing of the great work done by keychera and Soham's answer.
You can use this as a bound script and it will add a menu item to the extensions menu:
// Code.gs
function exportSheetsToDrive() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheets = ss.getSheets();
if (sheets === undefined || sheets.length === 0) {
return;
}
const now = new Date();
const csvBlobs = sheets.map((sheet) => {
const name = sheet.getName();
const csv = convertSheetToCsv(sheet);
Logger.log({ name, length: csv.length });
return Utilities.newBlob(csv, MimeType.CSV, `${name}.csv`)
});
const zipName = `export_${ss.getName()}_${now.toISOString()}.zip`;
const zip = Utilities.zip(csvBlobs, zipName);
DriveApp.createFile(zip);
}
function convertSheetToCsv(sheet) {
return sheet
.getDataRange()
.getValues()
.map((row) =>
row
.map((value) => value.toString())
.map((value) =>
value.includes("\n") || value.includes(",")
? '"' + value + '"'
: value
)
.join(",")
)
.join("\n");
}
and
// Menu.gs
function onOpen(e) {
const menu = SpreadsheetApp.getUi().createAddonMenu();
menu
.addItem('Export all sheets as CSV to Drive', 'exportSheetsToDrive')
.addToUi();
}
function onInstall(e) {
onOpen(e);
}

Related

Extract URL from copied text in Google Sheets [duplicate]

I have a sheet where hyperlink is set in cell, but not through formula. When clicked on the cell, in "fx" bar it only shows the value.
I searched on web but everywhere, the info is to extract hyperlink by using getFormula().
But in my case there is no formula set at all.
I can see hyperlink as you can see in image, but it's not there in "formula/fx" bar.
How to get hyperlink of that cell using Apps Script or any formula?
When a cell has only one URL, you can retrieve the URL from the cell using the following simple script.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var url = sheet.getRange("A2").getRichTextValue().getLinkUrl(); //removed empty parentheses after getRange in line 2
Source: https://gist.github.com/tanaikech/d39b4b5ccc5a1d50f5b8b75febd807a6
When Excel file including the cells with the hyperlinks is converted to Google Spreadsheet, such situation can be also seen. In my case, I retrieve the URLs using Sheets API. A sample script is as follows. I think that there might be several solutions. So please think of this as one of them.
When you use this script, please enable Sheets API at Advanced Google Services and API console. You can see about how to enable Sheets API at here.
Sample script:
var spreadsheetId = "### spreadsheetId ###";
var res = Sheets.Spreadsheets.get(spreadsheetId, {ranges: "Sheet1!A1:A10", fields: "sheets/data/rowData/values/hyperlink"});
var sheets = res.sheets;
for (var i = 0; i < sheets.length; i++) {
var data = sheets[i].data;
for (var j = 0; j < data.length; j++) {
var rowData = data[j].rowData;
for (var k = 0; k < rowData.length; k++) {
var values = rowData[k].values;
for (var l = 0; l < values.length; l++) {
Logger.log(values[l].hyperlink) // You can see the URL here.
}
}
}
}
Note:
Please set spreadsheetId.
Sheet1!A1:A10 is a sample. Please set the range for your situation.
In this case, each element of rowData is corresponding to the index of row. Each element of values is corresponding to the index of column.
References:
Method: spreadsheets.get
If this was not what you want, please tell me. I would like to modify it.
Hey all,
I hope this helps you save some dev time, as it was a rather slippery one to pin down...
This custom function will take all hyperlinks in a Google Sheets cell, and return them as text formatted based on the second parameter as either [JSON|HTML|NAMES_ONLY|URLS_ONLY].
Parameters:
cellRef : You must provide an A1 style cell reference to a cell.
Hint: To do this within a cell without hard-coding
a string reference, you can use the CELL function.
eg: "=linksToTEXT(CELL("address",C3))"
style : Defines the formatting of the output string.
Valid arguments are : [JSON|HTML|NAMES_ONLY|URLS_ONLY].
Sample Script
/**
* Custom Google Sheet Function to convert rich-text
* links into Readable links.
* Author: Isaac Dart ; 2022-01-25
*
* Params
* cellRef : You must provide an A1 style cell reference to a cell.
* Hint: To do this within a cell without hard-coding
* a string reference, you can use the CELL function.
* eg: "=linksToTEXT(CELL("address",C3))"
*
* style : Defines the formatting of the output string.
* Valid arguments are : [JSON|HTML|NAMES_ONLY|URLS_ONLY].
*
*/
function convertCellLinks(cellRef = "H2", style = "JSON") {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getRange(cellRef).getCell(1,1);
var runs = cell.getRichTextValue().getRuns();
var ret = "";
var lf = String.fromCharCode(10);
runs.map(r => {
var _url = r.getLinkUrl();
var _text = r.getText();
if (_url !== null && _text !== null) {
_url = _url.trim(); _text = _text.trim();
if (_url.length > 0 && _text.length > 0) {
switch(style.toUpperCase()) {
case "HTML": ret += '' + _text + '}' + lf; break;
case "TEXT": ret += _text + ' : "' + _url + '"' + lf; break;
case "NAMES_ONLY" : ret += _text + lf; break;
case "URLS_ONLY" : ret += _url + lf; break;
//JSON default : ...
default: ret += (ret.length>0?(','+ lf): '') +'{name : "' + _text + '", url : "' + _url + '"}' ; break;
}
ret += lf;
}
}
});
if (style.toUpperCase() == "JSON") ret = '[' + ret + ']';
//Logger.log(ret);
return ret;
}
Cheers,
Isaac
I tried solution 2:
var urls = sheet.getRange('A1:A10').getRichTextValues().map( r => r[0].getLinkUrl() ) ;
I got some links, but most of them yielded null.
I made a shorter version of solution 1, which yielded all the links.
const id = SpreadsheetApp.getActive().getId() ;
let res = Sheets.Spreadsheets.get(id,
{ranges: "Sheet1!A1:A10", fields: "sheets/data/rowData/values/hyperlink"});
var urls = res.sheets[0].data[0].rowData.map(r => r.values[0].hyperlink) ;

Zapier: BigCommerce to Google Sheet, New Row for Each Item

I have successfully linked my BigCommerce account to my Google Sheets (Drive) account so every time I receive a new order in my store the order is automatically exported into a Google Sheet. Unfortunately, an entire order is listed on one row with multiple items added into one cell. What I need is to have each product on its own row; for example, if someone orders three different products Zapier would create three new rows. This functionality exists when directly exporting orders from BigCommerce, but the "Zap" does not use the BigCommerce export function when pulling order information from my store to the Google Sheet.
I know this is a shot in the dark, but I am hoping someone might have a solution that I can implement. Thank you for your help!
I have created a script that perhaps could be used or modified, at least until you find if the process can be done within Zapier.
You can try the script in the following ss: https://docs.google.com/spreadsheets/d/1ggNYlLEeN3UYtZC_KlOGwpyII9CzOLKMnIOKIDrPJPM/edit?usp=sharing
The script assumes that orders arrive in the tab named Zapier. As things are set up, you would run the script through the Custom Menu.
If there are 2 orders or more, click the menu for each order.
The complete rows appear in the sheet FullList.
(if you want to play/try again, you will have to manually delete the rows in FullList once they are showing).
function processForNewOrders() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName('Zapier');
var destinationSheet = ss.getSheetByName('FullList');
var sourceValues = sourceSheet.getDataRange().getValues();
var destinationValues = destinationSheet.getDataRange().getValues();
var index = [];
destinationValues.forEach( function (x) {
index.push(x[0]);
})
var newOrders = [];
for (var y = sourceValues.length -1 ; y > 0 ; y --){
if(sourceValues[y][0].toString().indexOf('po_number') != -1 ) continue;
var i = index.indexOf(sourceValues[y][0]);
if(i != -1) break; // This readies only the fresh orders for processing
newOrders.push(sourceValues[y]);
}
Logger.log(newOrders)
for (var j = 0 ; j < newOrders.length ; j++){
var output = [];
var orderLine = newOrders[j];
Logger.log('orderLine = ' + orderLine);
var circuit = 0;
var items = 1
while (circuit < items){
var row = [];
for (var z = 0 ; z < orderLine.length; z++){
var cell = orderLine[z];
// Logger.log(cell);
var lines = cell.toString().split(',');
if(lines.length > 1) items = lines.length;
row.push(lines[circuit] ? lines[circuit] : lines[0]);
// Logger.log('row =' + row);
}
circuit ++;
Logger.log('circuit circuit circuit =' + circuit)
output.push(row);
}
}
Logger.log(output);
if(output != undefined)
destinationSheet.getRange(index.length+1,1,output.length,output[0].length).setValues(output);
}
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('Custom Menu')
.addItem('Process new order', 'processForNewOrders')
.addToUi();
}

How to compare two column in a spreadsheet

I have 30 columns and 1000 rows, I would like to compare column1 with another column. IF the value dont match then I would like to colour it red. Below is a small dataset in my spreadsheet:
A B C D E F ...
1 name sName email
2
3
.
n
Because I have a large dataset and I want to storing my columns in a array, the first row is heading. This is what I have done, however when testing I get empty result, can someone correct me what I am doing wrong?
var index = [];
var sheet = SpreadsheetApp.getActiveSheet();
function col(){
var data = sheet.getDataRange().getValues();
for (var i = 1; i <= data.length; i++) {
te = index[i] = data[1];
Logger.log(columnIndex[i])
if (data[3] != data[7]){
// column_id.setFontColor('red'); <--- I can set the background like this
}
}
}
From the code you can see I am scanning whole spreadsheet data[1] get the heading and in if loop (data[3] != data[7]) compare two columns. I do have to work on my colour variable but that can be done once I get the data that I need.
Try to check this tutorial if it can help you with your problem. This tutorial use a Google AppsScript to compare the two columns. If differences are found, the script should point these out. If no differences are found at all, the script should put out the text "[id]". Just customize this code for your own function.
Here is the code used to achieve this kind of comparison
function stringComparison(s1, s2) {
// lets test both variables are the same object type if not throw an error
if (Object.prototype.toString.call(s1) !== Object.prototype.toString.call(s2)){
throw("Both values need to be an array of cells or individual cells")
}
// if we are looking at two arrays of cells make sure the sizes match and only one column wide
if( Object.prototype.toString.call(s1) === '[object Array]' ) {
if (s1.length != s2.length || s1[0].length > 1 || s2[0].length > 1){
throw("Arrays of cells need to be same size and 1 column wide");
}
// since we are working with an array intialise the return
var out = [];
for (r in s1){ // loop over the rows and find differences using diff sub function
out.push([diff(s1[r][0], s2[r][0])]);
}
return out; // return response
} else { // we are working with two cells so return diff
return diff(s1, s2)
}
}
function diff (s1, s2){
var out = "[ ";
var notid = false;
// loop to match each character
for (var n = 0; n < s1.length; n++){
if (s1.charAt(n) == s2.charAt(n)){
out += "–";
} else {
out += s2.charAt(n);
notid = true;
}
out += " ";
}
out += " ]"
return (notid) ? out : "[ id. ]"; // if notid(entical) return output or [id.]
}
For more information, just check the tutorial link above and this SO question on how to compare two Spreadsheets.

Google Sheets: Exporting to TSV

I would like to create a function that automatically exports the current sheet to a TSV file on Google Sheets. Is it possible to export to a tab separated values file using Google Apps Script, i.e. like convertRangeToCsvFile for CSV?
I found this code for csv files by Michael Derazon and modified it for tsv files. I hope it helps.
/*
* script to export data in all sheets in the current spreadsheet as individual csv files
* files will be named according to the name of the sheet
* author: Michael Derazon
*/
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var tsvMenuEntries = [{name: "export as TSV files", functionName: "saveAstsv"}];
ss.addMenu("tsv", tsvMenuEntries);
};
function saveAstsv() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
// create a folder from the name of the spreadsheet
var folder = DriveApp.createFolder(ss.getName().toLowerCase().replace(/ /g,'_') + '_tsv_' + new Date().getTime());
for (var i = 0 ; i < sheets.length ; i++) {
var sheet = sheets[i];
// append ".tsv" extension to the sheet name
fileName = sheet.getName() + ".tsv";
// convert all available sheet data to tsv format
var tsvFile = convertRangeTotsvFile_(fileName, sheet);
// create a file in the Docs List with the given name and the tsv data
folder.createFile(fileName, tsvFile);
}
Browser.msgBox('Files are waiting in a folder named ' + folder.getName());
}
function convertRangeTotsvFile_(tsvFileName, sheet) {
// get available data range in the spreadsheet
var activeRange = sheet.getDataRange();
try {
var data = activeRange.getValues();
var tsvFile = undefined;
// loop through the data in the range and build a string with the tsv data
if (data.length > 1) {
var tsv = "";
for (var row = 0; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
if (data[row][col].toString().indexOf("\t") != -1) {
data[row][col] = "\"" + data[row][col] + "\"";
}
}
// join each row's columns
// add a carriage return to end of each row, except for the last one
if (row < data.length-1) {
tsv += data[row].join("\t") + "\r\n";
}
else {
tsv += data[row].join("\t");
}
}
tsvFile = tsv;
}
return tsvFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}

Hash of a cell text in Google Spreadsheet

How can I compute a MD5 or SHA1 hash of text in a specific cell and set it to another cell in Google Spreadsheet?
Is there a formula like =ComputeMD5(A1) or =ComputeSHA1(A1)?
Or is it possible to write custom formula for this? How?
Open Tools > Script Editor then paste the following code:
function MD5 (input) {
var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, input);
var txtHash = '';
for (i = 0; i < rawHash.length; i++) {
var hashVal = rawHash[i];
if (hashVal < 0) {
hashVal += 256;
}
if (hashVal.toString(16).length == 1) {
txtHash += '0';
}
txtHash += hashVal.toString(16);
}
return txtHash;
}
Save the script after that and then use the MD5() function in your spreadsheet while referencing a cell.
This script is based on Utilities.computeDigest() function.
Thanks to gabhubert for the code.
This is the SHA1 version of that code (very simple change)
function GetSHA1(input) {
var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_1, input);
var txtHash = '';
for (j = 0; j <rawHash.length; j++) {
var hashVal = rawHash[j];
if (hashVal < 0)
hashVal += 256;
if (hashVal.toString(16).length == 1)
txtHash += "0";
txtHash += hashVal.toString(16);
}
return txtHash;
}
Ok, got it,
Need to create custom function as explained in
http://code.google.com/googleapps/appsscript/articles/custom_function.html
And then use the apis as explained in
http://code.google.com/googleapps/appsscript/service_utilities.html
I need to handtype the complete function name so that I can see the result in the cell.
Following is the sample of the code that gave base 64 encoded hash of the text
function getBase64EncodedMD5(text)
{
return Utilities.base64Encode( Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, text));
}
The difference between this solution and the others is:
It fixes an issue some of the above solution have with offsetting the output of Utilities.computeDigest (it offsets by 128 instead of 256)
It fixes an issue that causes some other solutions to produce the same hash for different inputs by calling JSON.stringify() on input before passing it to Utilities.computeDigest()
function MD5(input) {
var result = "";
var byteArray = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, JSON.stringify(input));
for (i=0; i < byteArray.length; i++) {
result += (byteArray[i] + 128).toString(16) + "-";
}
result = result.substring(result, result.length - 1); // remove trailing dash
return result;
}
to get hashes for a range of cells, add this next to gabhubert's function:
function RangeGetMD5Hash(input) {
if (input.map) { // Test whether input is an array.
return input.map(GetMD5Hash); // Recurse over array if so.
} else {
return GetMD5Hash(input)
}
}
and use it in cell this way:
=RangeGetMD5Hash(A5:X25)
It returns range of same dimensions as source one, values will spread down and right from cell with formulae.
It's universal single-value-function to range-func conversion method (ref), and it's way faster than separate formuleas for each cell; in this form, it also works for single cell, so maybe it's worth to rewrite source function this way.
Based on #gabhubert but using array operations to get the hexadecimal representation
function sha(str){
return Utilities
.computeDigest(Utilities.DigestAlgorithm.SHA_1, str) // string to digested array of integers
.map(function(val) {return val<0? val+256 : val}) // correct the offset
.map(function(val) {return ("00" + val.toString(16)).slice(-2)}) // add padding and enconde
.join(''); // join in a single string
}
Using #gabhubert answer, you could do this, if you want to get the results from a whole row. From the script editor.
function GetMD5Hash(value) {
var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, value);
var txtHash = '';
for (j = 0; j <rawHash.length; j++) {
var hashVal = rawHash[j];
if (hashVal < 0)
hashVal += 256;
if (hashVal.toString(16).length == 1)
txtHash += "0";
txtHash += hashVal.toString(16);
}
return txtHash;
}
function straightToText() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var r = 1;
var n_rows = 9999;
var n_cols = 1;
var column = 1;
var sheet = ss[0].getRange(r, column, n_rows, ncols).getValues(); // get first sheet, a1:a9999
var results = [];
for (var i = 0; i < sheet.length; i++) {
var hashmd5= GetMD5Hash(sheet[i][0]);
results.push(hashmd5);
}
var dest_col = 3;
for (var j = 0; j < results.length; j++) {
var row = j+1;
ss[0].getRange(row, dest_col).setValue(results[j]); // write output to c1:c9999 as text
}
}
And then, from the Run menu, just run the function straightToText() so you can get your result, and elude the too many calls to a function error.
I was looking for an option that would provide a shorter result. What do you think about this? It only returns 4 characters. The unfortunate part is that it uses i's and o's which can be confused for L's and 0's respectively; with the right font and in caps it wouldn't matter much.
function getShortMD5Hash(input) {
var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, input);
var txtHash = '';
for (j = 0; j < 16; j += 8) {
hashVal = (rawHash[j] + rawHash[j+1] + rawHash[j+2] + rawHash[j+3]) ^ (rawHash[j+4] + rawHash[j+5] + rawHash[j+6] + rawHash[j+7])
if (hashVal < 0)
hashVal += 1024;
if (hashVal.toString(36).length == 1)
txtHash += "0";
txtHash += hashVal.toString(36);
}
return txtHash.toUpperCase();
}
I needed to get a hash across a range of cells, so I run it like this:
function RangeSHA256(input)
{
return Array.isArray(input) ?
input.map(row => row.map(cell => SHA256(cell))) :
SHA256(input);
}

Resources