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);
}
}
Related
I need to search for string or list of strings Snowflake table using Java API and fetch all the matching rows and display in Angular UI. I am using dynamic SQL (like operator) to generate the query using information schema. I have created this stored procedure and its working. Do we have any better approach or any architectural patterns for this particular usecase.
Based on the search option (starts/ends with) decide the start and end character to be used with Like operator
Get all the varchar,char columns from the table by joining with information schema.
Build dynamic sql with these columns.
Build the json array based on the query result.
CREATE OR REPLACE PROCEDURE SEARCH_DATA(SCHEMA_NAME VARCHAR, TABLE_NAME VARCHAR, SEARCH_OPTION VARCHAR, KEYWORDS ARRAY)
RETURNS VARIANT
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS '
var searchStart = "";
var searchEnd = "";
if (SEARCH_OPTION == "Starts with")
{
searchEnd = "%";
}
else if (SEARCH_OPTION == "Ends with")
{
searchStart = "%";
}
else if (SEARCH_OPTION == "Contains")
{
searchStart = "%";
searchEnd = "%";
}
// Dynamically compose the SQL statement to execute.
var sqlCommand = "select c.column_name from information_schema.COLUMNS c JOIN information_schema.TABLES T ON T.table_name = c.table_name AND T.table_schema = c.table_schema WHERE T.table_schema = ''";
sqlCommand+= SCHEMA_NAME;
sqlCommand+= "'' AND T.table_name= ''";
sqlCommand+= TABLE_NAME;
sqlCommand+= "'' AND c.data_type NOT IN (''TIMESTAMP_TZ'',''BOOLEAN'',''NUMBER'') ORDER BY ordinal_position";
// Prepare statement.
var stmt = snowflake.createStatement({ sqlText: sqlCommand });
// Execute Statement
var rs = stmt.execute();
var columnArray = [];
var columnName = "";
while (rs.next())
{
columnName = rs.getColumnValue(''COLUMN_NAME'');
columnArray.push(columnName);
}
var queryPrefix = "SELECT ''dummy''"
for(var i=0; i< columnArray.length; i++)
{
queryPrefix += "," + columnArray[i];
}
queryPrefix+= " FROM " + SCHEMA_NAME + "." + TABLE_NAME;
var query = "";
for(var j=0; j< KEYWORDS.length; j++)
{
query += queryPrefix;
query += " WHERE (1=0";
for(var i=0; i< columnArray.length; i++)
{
query += " OR "+ columnArray[i] + " LIKE ''" + searchStart + KEYWORDS[j] + searchEnd + "''";
}
query += ")";
if(j < KEYWORDS.length - 1)
query += " UNION ";
}
// Prepare statement.
stmt = snowflake.createStatement({ sqlText: query });
// Execute Statement
rs = stmt.execute();
var resultArray = [];
var row_as_json = {};
while (rs.next())
{
// Put each row in a variable of type JSON.
row_as_json = {};
// For each column in the row...
for (var i=0; i< columnArray.length; i++)
{
row_as_json[columnArray[i]] = rs.getColumnValue(columnArray[i]);
}
// Add the row to the array of rows.
resultArray.push(row_as_json);
}
return resultArray;
';
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) ;
I'm working on a R&D project where I collect my server side statistics using Linux commands and output the same to my mail. Now my plan is to read and parse the Gmail content which has my data of the server (as below) and extract it to Google Sheets. My Gmail content looks like below which has data in rows and columns.
Date&Time JVM PID CPU MEM FGC
03-09-2017-09-08-PM abc01_xzy01 12345 1.2% 2.75 3
03-09-2017-09-08-PM abc01_xzy01 12345 3.5% 2.71 4
03-09-2017-09-08-PM abc01_xzy01 12345 4.6% 2.79 5
My idea here is to pull exactly the same way into a Google Sheet. I'm using the below code but it's unable to read the content. I'm thinking if it's related to the RegExp used. Could you please tell me how exactly the code has to be written in the if statement.
function parseEmailMessages(start) {
start = start || 0;
var threads = GmailApp.getInboxThreads(start, 100);
var sheet = SpreadsheetApp.getActiveSheet();
for (var i = 0; i < threads.length; i++) {
var tmp,
message = threads[i].getMessages()[0],
// subject = message.getSubject(),
subject = "SERVER TEST REPORT",
content = message.getPlainBody();
if (content) {
tmp = content.match(/Date&Time:\s*([A-Za-z0-9\s]+)(\r?\n)/);
var username = (tmp && tmp[1]) ? tmp[1].trim() : 'No Date & Time';
tmp = content.match(/JVM:\s*([A-Za-z0-9\s]+)(\r?\n)/);
var username = (tmp && tmp[1]) ? tmp[1].trim() : 'No JVM';
sheet.appendRow([username, email, subject, comment]);
}
}
}
My output just prints No Date & Time and No JVM in the Google Sheets. I want to print the names and data in the columns and rows as given in the mail. Can someone please help me on this and tell me what is the mistake and how to pull the data like expected. Thanks in advance.
If all that is in your email is what you posted above, this will put it in your spreadsheet. Change the email subject and sheet name to suit your needs.
function getGmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var threads = GmailApp.search('IMPORTANT1', 0, 10);
for (var i = 0; i < threads.length; i++) {
var messages = GmailApp.getMessagesForThread(threads[i]);
for (var j = 0; j < messages.length; j++) {
var msg=messages[j].getPlainBody();
var msg=msg.trim()
}
}
result1(msg)
}
function result1(range) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Sheet1")
var lr=sheet1.getLastRow()
var output=[]
var line=range.split("\n")
for(j=0;j<line.length;j++){
output.push(line[j].split(" "))
}
sheet1.getRange(lr+1, 1, output.length, output[0].length).setValues(output)
}
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();
}
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);
}