Sheets API Import sheet with formating - google-apps-script

Using the script here I am able to import the sheet to another document however it does not work if I remove the columns from the srcRange. My goal is to copy the entire sheets formating over to the other sheet and the number of columns occasionally changes;
This works but the columns have to be specified:
const srcSpreadsheetId = "1mVlva8Dyxxxxxxxxxxxxxx"; // Please set source Spreadsheet ID.
const dstSpreadsheetId = "1a2Eb7fQOxxxxxxxxxxxxxx"; // Please set destination Spreadsheet ID.
const srcRange = "Database!A:I";
const dstRange = "Database";
// Here, the date object is retrieved as the serial number.
const values = Sheets.Spreadsheets.Values.get(srcSpreadsheetId, srcRange, { dateTimeRenderOption: "SERIAL_NUMBER", valueRenderOption: "UNFORMATTED_VALUE" }).values;
const dstSheet = SpreadsheetApp.openById(dstSpreadsheetId).getSheetByName(dstRange);
const sheetId = dstSheet.getSheetId();
Sheets.Spreadsheets.batchUpdate({ requests: [{ repeatCell: { range: { sheetId }, fields: "userEnteredValue" } }] }, dstSpreadsheetId);
Sheets.Spreadsheets.Values.update({ values }, dstSpreadsheetId, dstRange, { valueInputOption: "USER_ENTERED" });
// Here, the number format is copied.
const numberFormats = SpreadsheetApp.openById(srcSpreadsheetId).getRange(srcRange).getNumberFormats();
dstSheet.getRange(1, 1, numberFormats.length, numberFormats[0].length).setNumberFormats(numberFormats);
This does not work:
const srcSpreadsheetId = "1mVlva8Dyxxxxxxxxxxxxxx"; // Please set source Spreadsheet ID.
const dstSpreadsheetId = "1a2Eb7fQOxxxxxxxxxxxxxx"; // Please set destination Spreadsheet ID.
const srcRange = "Database"; // <<<<<<<<<<<<<<<<< Columns not specified
const dstRange = "Database";
// Here, the date object is retrieved as the serial number.
const values = Sheets.Spreadsheets.Values.get(srcSpreadsheetId, srcRange, { dateTimeRenderOption: "SERIAL_NUMBER", valueRenderOption: "UNFORMATTED_VALUE" }).values;
const dstSheet = SpreadsheetApp.openById(dstSpreadsheetId).getSheetByName(dstRange);
const sheetId = dstSheet.getSheetId();
Sheets.Spreadsheets.batchUpdate({ requests: [{ repeatCell: { range: { sheetId }, fields: "userEnteredValue" } }] }, dstSpreadsheetId);
Sheets.Spreadsheets.Values.update({ values }, dstSpreadsheetId, dstRange, { valueInputOption: "USER_ENTERED" });
// Here, the number format is copied.
const numberFormats = SpreadsheetApp.openById(srcSpreadsheetId).getRange(srcRange).getNumberFormats();
dstSheet.getRange(1, 1, numberFormats.length, numberFormats[0].length).setNumberFormats(numberFormats);
Have tried multiple variations to no avail. Keep getting > Exception: Range not found.

If you want to achieve The source range only works right now if you put in the columns but I want to copy the format for the entire sheet. and your actual error is No error > const srcRange = "Database!A:IX";, error happens if I use > const srcRange = "Database";, how about the following modification?
From:
const srcRange = "Database!A:IX";
To:
const srcRange = "Database";
And,
From:
const numberFormats = SpreadsheetApp.openById(srcSpreadsheetId).getRange(srcRange).getNumberFormats();
To:
const numberFormats = SpreadsheetApp.openById(srcSpreadsheetId).getSheetByName(srcRange).getDataRange().getNumberFormats();
or
const srcSheet = SpreadsheetApp.openById(srcSpreadsheetId).getSheetByName(srcRange);
const numberFormats = srcSheet.getRange(1, 1, srcSheet.getMaxRows(), srcSheet.getMaxColumns()).getNumberFormats();
Added:
From I tested both the first and second solution you suggested with a smaller amount of data and both work. The only wierd part is that for numbers stored as text they are in a white font and you cannot change the color., when I saw your Spreadsheet, I understood that the reason for this issue is due to plain text format. When the cells of plain text are retrieved with getNumberFormats(), it seems that null` is returned. By this, the number is not displayed. In order to reflect this issue, please modify it as follows.
Modified script:
Please set the Spreadsheet IDs and sheet names for your test situation.
function sample() {
const srcSpreadsheetId = "1mVlva8Dyxxxxxxxxxxxxxx"; // Please set source Spreadsheet ID.
const dstSpreadsheetId = "1a2Eb7fQOxxxxxxxxxxxxxx"; // Please set destination Spreadsheet ID.
const srcRange = "Database"; // <<<<<<<<<<<<<<<<< Columns not specified
const dstRange = "Database";
// Here, the date object is retrieved as the serial number.
const values = Sheets.Spreadsheets.Values.get(srcSpreadsheetId, srcRange, { dateTimeRenderOption: "SERIAL_NUMBER", valueRenderOption: "UNFORMATTED_VALUE" }).values;
const dstSheet = SpreadsheetApp.openById(dstSpreadsheetId).getSheetByName(dstRange);
const sheetId = dstSheet.getSheetId();
Sheets.Spreadsheets.batchUpdate({ requests: [{ repeatCell: { range: { sheetId }, fields: "userEnteredValue" } }] }, dstSpreadsheetId);
Sheets.Spreadsheets.Values.update({ values }, dstSpreadsheetId, dstRange, { valueInputOption: "USER_ENTERED" });
// Here, the number format is copied.
const srcSheet = SpreadsheetApp.openById(srcSpreadsheetId).getSheetByName(srcRange);
const range = srcSheet.getRange(1, 1, srcSheet.getMaxRows(), srcSheet.getMaxColumns());
const numberFormats = range.getNumberFormats().map(r => r.map(c => c || "#"));
const styles = range.getTextStyles();
dstSheet.getRange(1, 1, numberFormats.length, numberFormats[0].length).setNumberFormats(numberFormats).setTextStyles(styles);
}

Related

Dynamic grouping in AppSript

I have a Spreadsheet with about 3000 rows which are grouped by order number. I'm trying to build an macro to:
remove all groups
multisort all rows
recreate groups
collapse all groups marked as finished orders (optional - have no idea how to achieve this )
SHEET_NAME = "PLAN";
SORT_DATA_RANGE = "A2:CJ";
GROUP_DATA_RANGE = "BQ2:BQ";
SORT_ORDER = [
{column: 40, ascending: false},
{column: 2, ascending: true},
{column: 4, ascending: true}
];
function Sortowanie() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
removeAllGroups();
multiSortColumns();
groupRows();
ss.toast('Zakończono.');
}
function multiSortColumns(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(SHEET_NAME);
var range = sheet.getRange(SORT_DATA_RANGE + sheet.getLastRow());
range.sort(SORT_ORDER);
ss.toast('Sortowanie zakończone.');
}
function removeAllGroups() {
const ss = SpreadsheetApp.getActive();
const ssId = ss.getId();
const sheet = ss.getSheetByName(SHEET_NAME);
const sheetId = sheet.getSheetId();
sheet.expandAllRowGroups();
const n = Sheets.Spreadsheets.get(ssId, { ranges: [SHEET_NAME] }).sheets[0].rowGroups.reduce((n, { depth }) => n < depth ? depth : n, 0);
const requests = Array(n).fill("").map(_ => ({ deleteDimensionGroup: { range: { sheetId, dimension: "ROWS" } } }));
Sheets.Spreadsheets.batchUpdate({ requests }, ssId);
ss.toast('Usuwanie grup zakończone.');
}
function groupRows() {
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName(SHEET_NAME);
const levels = sheet.getRange(GROUP_DATA_RANGE + getLastRowSpecial()).getValues();
const sheetId = sheet.getSheetId();
const requests = levels.flatMap(([a], i) => Array(a).fill("").map(_ => ({ addDimensionGroup: { range: { sheetId, startIndex: i + 1, endIndex: i + 2, dimension: "ROWS" } } })));
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
ss.toast('Ponowne grupowanie zakończone.');
}
function getLastRowSpecial() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheet = ss.getSheetByName(SHEET_NAME);
const lastRow = sheet.getRange(GROUP_DATA_RANGE).getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow();
return lastRow
};
Everything works perfectly when I limit range to 1000 rows. When I try to run this for a whole range or more than 1000 and debuging, I'm getting this:
HttpResponseException: Response Code: 413. Message: response too large.
Without debugger after a while I'm getting this:
The JavaScript runtime has unexpectedly terminated.
Most bizarre thing is that script is creating these groups for a whole range and then error comes out after a while.
I think that 3K rows is not a big range for that, maybe someone has an idea what is wrong?
Here it is sample data sheet:
https://docs.google.com/spreadsheets/d/1DLXxZVyrhDxrBe1AX3iy54nQTFVJkoIpeos7M9mEaIo/edit?usp=sharing
Issue:
If I understand your situation correctly:
You are able to remove existing groups and sort the rows according to the groups you want to create.
The groups are defined by a column with 0s and 1s, so that 0 refers to group headers and continguous 1s should be grouped under the same group.
I assume here that, after the column header (first row), all values in your column are 0 or 1.
Solution:
Use slice and findIndex to find the successive group headers, looking iteratively for the values different than 1, so that all the rows in between are part of the same group.
For each iteration, use the indexes of the current and the next header to build each request.
Code sample:
const FIRST_ROW = 2;
function groupRows() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(SHEET_NAME);
const levels = sheet.getRange(GROUP_DATA_RANGE + getLastRowSpecial()).getValues().flat();
const sheetId = sheet.getSheetId();
const requests = [];
let currentHeader = FIRST_ROW;
while (currentHeader < levels.length) {
let groupLength = levels.slice(currentHeader-FIRST_ROW+1).findIndex(l => l !== 1);
if (groupLength < 0) groupLength = levels.length - currentHeader + 1;
const nextHeader = groupLength + currentHeader+1;
const request = { addDimensionGroup: { range: { sheetId, startIndex: currentHeader, endIndex: nextHeader-1, dimension: "ROWS" } } };
requests.push(request);
currentHeader = nextHeader;
}
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
ss.toast('Ponowne grupowanie zakończone.');
}
Note:
Your other functions are not displayed here, but you should use them to remove existing rows and to make sure your data is sorted according to your preferences.
One of you problems but probably not the only problem
Range specified like this generate a lot of nulls between last row and max rows
"BQ2:BQ"
Try rewriting them like this:
"BQ2:BQ" + sheet.getLastRow()

Find Edit and Update - Search value from another spreadsheet and display to source sheet (Datasheet to Userform)

Search function, If B1(UserForm) is equal A:A (Datasheet), then get relevant value from Datasheet to Userform to corresponding cells ["C3", "C7", "C8", "D8", "D6", "D4", "E8", "E19", "E20", "E21", "E22", "B10:E18", "B19: C22"] for editing and update purpose.
I have attached a Screenshot explaining with color code where the data should go. https://i.stack.imgur.com/Yrfje.jpg [SCREENSHOT][1]
Also Shared two spreadsheets with actual data (Userform and Datasheet) for your reference https://docs.google.com/spreadsheets/d/1NY_ckzEWxU7DCGro5tTqzpiOi6iG5PAQFxpZg0OKodY/edit?usp=sharing
https://docs.google.com/spreadsheets/d/1QL0jaNts2YRkZTlxmS0bk7V1fVVHBsJFmxS5C05PEmA/edit?usp=sharing
Maybe I need some different script to achieve this task
var ss = SpreadsheetApp.getActiveSpreadsheet();
var form_sheet = ss.getSheetByName('UserForm');
var num = form_sheet.getRange('b1').getValue();
var data_sheet = ss.getSheetByName('DataSheet');
var nums = data_sheet.getRange('a:a').getValues().flat();
var row = nums.indexOf(num);
if (row < 0) {
ss.toast('Nothing was found')
return;
}
row++;
var data1 = data_sheet.getRange('a' + row + ':g' + row).getValues().flat();
var data2 = JSON.parse(data1.pop());
form_sheet.getRange('c3:c8').clearContent().setValues(data1.map(x => [x]));
form_sheet.getRange('b10:e20').clearContent().setValues(data2)
}
thank you much #Tanaike
This script works
function mainsearch() {
const srcSpreadsheetId = "1QL0jaNts2YRkZTlxmS0bk7V1fVVHBsJFmxS5C05PEmA"; // Please set the source Spreadsheet ID (WB-DataSheet).
const srcSheetName = "DataSheet";
const dstSheetName = "UserForm";
// Retrieve values from source sheet and create an array and search value.
const dstSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const dstSheet = dstSpreadsheet.getSheetByName(dstSheetName);
const search = dstSheet.getRange("B1").getValue();
// Search the value.
const srcSpreadsheet = SpreadsheetApp.openById(srcSpreadsheetId);
const srcSheet = srcSpreadsheet.getSheetByName(srcSheetName);
const range = srcSheet.getRange("A2:A" + srcSheet.getLastRow()).createTextFinder(search).findNext();
if (!range) {
SpreadsheetApp.getUi().alert('UserForm Number Not Found');
}
// Retrieve the values from the searched row.
const values = srcSheet.getRange(range.getRow(), 1, 1, srcSheet.getLastColumn()).getValues()[0];
// Put the values to the cells of "UserForm" sheet.
const expandRangeList = ["C3", "C7", "C8", "D8", "D6", "D4", "E8", "E19", "E20", "E21", "E22", "B10", "C10", "D10", "E10", "B11", "C11", "D11", "E11", "B12", "C12", "D12", "E12", "B13", "C13", "D13", "E13", "B14", "C14", "D14", "E14", "B15", "C15", "D15", "E15", "B16", "C16", "D16", "E16", "B17", "C17", "D17", "E17", "B18", "C18", "D18", "E18", "B19", "C19", "B20", "C20", "B21", "C21", "B22", "C22","D19", "D20", "D21", "D22"];
const data = expandRangeList.map((range, i) => ({ range, values: [[values[i] || ""]] }));
Sheets.Spreadsheets.Values.batchUpdate({ data, valueInputOption: "USER_ENTERED" }, dstSpreadsheet.getId());
}```

Group rows with appscript

I have a sheet with more than 3000 rows and I want to group those rows by a parameter in ColA. So all rows having '1' in colA should be grouped under the row above with '0' in colA. Once groups are created I want them to collapse.
Since the script will be triggered daily upon data update, I also need the before-created groups to be removed so the new ones can be properly created.
I have several scrips doing what I need but it takes forever for them to go through all the rows. Is it possible to optimize them in some way or perhaps a different approach can be used for my needs? Thanks in advance for your help!
function removeAllGroups1() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Pipeline");
const rg = sh.getDataRange();
const vs = rg.getValues();
vs.forEach((r, i) => {
let d = sh.getRowGroupDepth(i + 1);
if (d >= 1) {
sh.getRowGroup(i + 1, d).remove()
}
});
}
function groupRows1() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Pipeline');
const levels = sh.getRange(2, 1, sh.getLastRow() - 1).getValues().flat();
levels.forEach((e, i) => sh.getRange(i + 2, 1).shiftRowGroupDepth(e));
}
function collapse() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Pipeline');
let lastRow = sh.getDataRange().getLastRow();
for (let row = 1; row < lastRow; row++) {
let depth = sh.getRowGroupDepth(row);
if (depth < 1) continue;
sh.getRowGroup(row, depth).collapse();
}
}
Data Sample: https://docs.google.com/spreadsheets/d/10BNrnAyQw89gy-Sj3CLiz4AgVtFI0AjXTvc0REGGTfY/edit#gid=113574154
I believe your goal is as follows.
You want to group the rows with high process speed.
You want to delete all groups with high process speed.
You want to collapse all groups with high process speed.
From the above goal, I thought that when Sheets API is used, the process cost can be reduced. And, Sheets API can group rows, delete all groups and collapse all groups.
Sample script:
Before you run this script, please enable Sheets API at Advanced Google services.
function removeAllGroups2() {
const sheetName = "sample";
const ss = SpreadsheetApp.getActive();
const ssId = ss.getId();
const sheetId = ss.getSheetByName(sheetName).getSheetId();
const n = Sheets.Spreadsheets.get(ssId, { ranges: [sheetName] }).sheets[0].rowGroups.reduce((n, { depth }) => n < depth ? depth : n, 0);
const requests = Array(n).fill("").map(_ => ({ deleteDimensionGroup: { range: { sheetId, dimension: "ROWS" } } }));
Sheets.Spreadsheets.batchUpdate({ requests }, ssId);
}
function groupRows2() {
const sheetName = "sample";
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName(sheetName);
const levels = sheet.getRange("A2:A" + sheet.getLastRow()).getValues();
const sheetId = sheet.getSheetId();
const requests = levels.flatMap(([a], i) => Array(a).fill("").map(_ => ({ addDimensionGroup: { range: { sheetId, startIndex: i + 1, endIndex: i + 2, dimension: "ROWS" } } })));
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
}
function collapse2() {
const ss = SpreadsheetApp.getActive();
const requests = Sheets.Spreadsheets.get(spreadsheetId, {ranges: ["sample"]}).sheets[0].rowGroups.map(r => {
r.collapsed = true;
return { updateDimensionGroup: { fields: "*", dimensionGroup: r }};
});
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
}
Note:
I tested these sample scripts using your sample Spreadsheet. So when the structure of the Spreadsheet is different from your sample Spreadsheet, these scripts might not be able to be used. Please be careful about this. At first, please test them using your sample script.
References:
Method: spreadsheets.get
Method: spreadsheets.batchUpdate
DeleteDimensionGroupRequest
AddDimensionGroupRequest
UpdateDimensionGroupRequest

Sheets API: Append Row with hidden columns

I have a "appendRow" function for insert rows in the Google sheet called "Data", and this sheet has a column called "Id" (**A** column) which is hidden by client request.
function appendRow() {
var spreadsheetId = "SPREADSHEET_ID";
var range = "Data!A2:C";
var resource = {
values: [
["1", "James", "jam10#gmail.com"]
]
};
Sheets.Spreadsheets.Values.append(resource, spreadsheetId, range, {
valueInputOption: "USER_ENTERED"
});
}
However, when I execute this function, the result is not the expected, as seen in the image.
I appreciate any information, idea or solution that can help me to resolve this incident. Regards.
function appendRow() {
const ss = SpreadsheetApp.openById("SS_ID");
const sh = ss.getSheetByName('Data');
sh.appendRow(["1", "James", "jam10#gmail.com"]);
}
//row must be 2d array
function appendRows(rows) {
var spreadsheetId = "SPREADSHEET_ID";
const ss = SpreadsheetApp.openById("SSID");
const sh = ss.getSheetByName('Data');
sh.getRange(sh.getLastRow() + 1, 1, rows.length, rows[0].length).setValues(rows);
}

how to add every value to data object in firebase?

Im using this code to push the values from my google sheet to firebase, but i only get the first row added. how can I make this dynamic? everytime there is a new row, there is a new entry created in firebase?
here is the code:
//Add in your database secret
var secret = ''
function getFirebaseUrl(jsonPath) {
return (
'https://gradetracker-789b7-default-rtdb.firebaseio.com/' +
jsonPath +
'.json?auth=' +
secret
)
}
var sheet = SpreadsheetApp.getActiveSheet()
var [rows, columns] = [sheet.getLastRow(),
sheet.getLastColumn()]
var data = sheet.getRange(1, 1, rows, columns).getValues();
console.log(data);
var dataObject = {};
for (var i=1; i <data.length; i++) {
var dataRow = data[i];
var uid = dataRow[0];
var ET1 = dataRow[1];
var ET2 = dataRow[2];
dataObject[uid]= {
et1 : ET1,
et2 : ET2,
}
}
my sheet data is this
enter image description here
My output right now is
enter image description here
but i need this to be
enter image description here
In your situation, how about the following sample script?
Sample script:
function myFunction() {
const spreadsheetId = "###"; // Please set the Spreadsheet ID.
const sheetName = "Sheet1"; // Please set the sheet name.
// 1. Retrieve values from Spreadsheet.
const sheet = SpreadsheetApp.openById(spreadsheetId).getSheetByName(sheetName);
const [[, h1, h2], ...data] = sheet.getDataRange().getValues();
// 2. Create an object for sending to Firestore.
const obj = data.map(([a, b, c]) => ({[a]: {[h1]: b, [h2]: c}}));
console.log(obj);
// 3. Send the object to Firestore.
// const firestore = ###; // Please set your `firestore`.
// const res = obj.map(r => firestore.createDocument("###document###", r));
}
When above script is run, obj is as follows.
[
{"###uid1###":{"et1":60,"et2":80}},
{"###uid2###":{"et1":90,"et2":65}}
]
Note:
In this modification, it supposes that the header (uid, et1 and et2) of your sample spreadsheet are the cells "A1", "B1" and "C1", respectively. Please be careful this.
Reference:
map()