Merging Two Ranges as One Range in Google App Script - javascript

I have a google app script.
code.gs
const doGet = _ => {
const ss = SpreadsheetApp.openById("1FJtOQZAN_SIOYHybA9FXbJ_ngoRy38KTvo1hZu7SEUs");
const sheetA = ss.getSheetByName("SYLLABUSC");
const rangeA = sheetA.getRange("E1:E5");
const sheetB = ss.getSheetByName("SHEETB");
const rangeB = sheetB.getRange("A1:A3");
const html = rangeA.getDisplayValues().join("\n");
return ContentService.createTextOutput(html).setMimeType(ContentService.MimeType.JAVASCRIPT);
}
By above I'm getting TextOutput from SheeA Range only.
But I want both ranges one by one, i.e.
SheetA Range and then SheetB Range.
How to merge these ..?
Update :
By the Answer provided, the Code.gs file modified like below.
const doGet = _ => {
const ss = SpreadsheetApp.openById("1FJtOQZAN_SIOYHybA9FXbJ_ngoRy38KTvo1hZu7SEUs");
const sheetA = ss.getSheetByName("SYLLABUSC");
const rangeA = sheetA.getRange("E1:E5").getDisplayValues();
const sheetB = ss.getSheetByName("SHEETB");
const rangeB = sheetB.getRange("A1:A3").getDisplayValues();
const html = rangeA.concat(rangeB).join("\n");
return ContentService.createTextOutput(html).setMimeType(ContentService.MimeType.JAVASCRIPT);
}
Here is My Web App

Replace this:
const html = rangeA.getDisplayValues().join("\n");
With this:
let html = rangeA.getDisplayValues().concat(rangeB.getDisplayValues());
html = html.join("\n");

Merge Ranges:
function mergeRanges() {
const ss = SpreadsheetApp.openById("1R5Wgq4okSWz0d159X5kPvOk8W1T_6eZ2kwoq5kdjO0w");
const sheetA = ss.getSheetByName("SYLLABUSC");
const vsa = sheetA.getRange("C1:C6").getDisplayValues();
const sheetB = ss.getSheetByName("SHEETB");
const vsb = sheetB.getRange("A1:A3").getDisplayValues();
const html = [vsa, vsb].reduce((a, c, i) => {
c.forEach(arr => a += arr.join('\n'))
return a;
}, "")
Logger.log(html);
return ContentService.createTextOutput(html).setMimeType(ContentService.MimeType.JAVASCRIPT);
}

Related

ExcelJS write an array to excel

I'm using an API where i fetch information and it stores in an array called res. I want this array to be written to an excel file. The first information in the array should be in A2 and the second in B2 etc etc.
This is my code:
const api2 = require("fordonsuppgifter-api-wrapper");
(async () => {
console.log("fetching vehicle info");
var res = await api2.GetVehicleInformation("XMP433");
console.log(res);
})();
const Excel = require('exceljs');
const fileName = 'simple.xlsx';
const wb = new Excel.Workbook();
const ws = wb.addWorksheet('My Sheet');
const r3 = ws.getRow(3);
r3.values = [1, 2, 3, 4, 5, 6];
wb.xlsx
.writeFile(fileName)
.then(() => {
console.log('file created');
})
.catch(err => {
console.log(err.message);
});
The API is working, but I can't get it to write to an excel file.
You can create excel file with both combination call.
The exceljs library need to moving cell write function
ws.getCell(<cell_address>).value = <assigned_value>
cell_address needs to increase number for moving down(increase row number)
Example: A1 -> A2 for next row.
The columnName() can convert from integer to Excel Column Address.
Example
columnName(1) -> A
columnName(2) -> B
You needs to convert Object() from JSON to Object type
Then can get the JSON key's list
Object.keys(carData)
Final Code
const Excel = require('exceljs');
const api2 = require("fordonsuppgifter-api-wrapper");
const columnName = (index) => {
var cname = String.fromCharCode(65 + ((index - 1) % 26));
if (index > 26)
cname = String.fromCharCode(64 + (index - 1) / 26) + cname;
return cname;
}
const getCarInformation = async (keyword) => {
try {
const res = await api2.GetVehicleInformation(keyword);
return Promise.resolve(res);
} catch (error) {
return Promise.reject(error);
}
}
// Search car by keyword
getCarInformation("XMP433")
.then((carData) => {
const fileName = 'simple.xlsx';
const workbook = new Excel.Workbook();
// make workbook with 'car' name
const ws = workbook.addWorksheet("car")
// Start Cell A1 for title column
let headerColumn = 1
let section_number = 1
for (let key in carData) {
// title column, example : A1 = Sammanfattning
ws.getCell(columnName(headerColumn) + String(section_number)).value = key
subItems = carData[key]
row_number = section_number
for (let subKey in subItems) {
// Sub title Cell Bx, example : B1 = Registreringsnummer
ws.getCell(columnName(headerColumn + 1) + String(row_number)).value = subKey
// value Cell Cx, example : C1 = '(2*) XMP433'
ws.getCell(columnName(headerColumn + 2) + String(row_number)).value = subItems[subKey]
row_number++;
}
// Jump to next title
section_number = section_number + Object.keys(carData[key]).length;
}
workbook.xlsx
.writeFile(fileName)
.then(() => {
console.log('file created');
})
.catch(err => {
console.log(err.message);
});
})
.catch(err => {
console.log(err.message);
});
Result in simple.xlsx
You need to click on header for expanding the width of a column to fit its contents after saved file.

Function: Error stackTraceLimit: 10 - JavaScript, Google AppScript

Forgive me for my silly questions, I am really a new bee for this coding world.
I have a google app script running on a page where it extracts data from a different sheet. Code so far works fine except it skips few set of code lines and give me an { [Function: Error] stackTraceLimit: 10 } . This skipping part is very important and appreciate id you guys can help.
Thanks. I will post the entire code below.
const bookAId = 'xxxxxxxxxxxxx'; // ssId of book A
const bookBId = 'yyyyyyyyyyyyy'; // ssId of book B
const sheetA = 'RS_Tharalsdson' // name of sheet in book A containing sheet names
const deadlineRange = 'G3'; // the cell in book B sheet i that you want to copy - deadline
const pmsRange = 'A3' // the cell in book B sheet i that you want to copy - pms
const pmsOnq = 'onq';
const pmsFosse = 'fosse'
const pmsGalaxy = 'galaxylightspeed'
const pmsOpera = 'opera'
const fileFormatCol = 4 // column D
const fileFormatRow = 6 // first row containing file formats
const operaCol = 6 // column F
const operaRow = 6 // first row of opera file formats
const subCol = 5 // submission method column of fosse,onq and galaxy
const subRow = 6 // submission starting row of fosse,onq and galaxy
const subColOpr = 4 // opera submission col
const subRowOpr = 6// opera submission starting row
function getFileFormat(){
const ssA = SpreadsheetApp.openById(bookAId);
const sA = ssA.getSheetByName(sheetA);
const sheetNames = sA.getRange('G2:G').getValues().reduce((names, row) => row[0] !== '' ? names.concat(row[0]) : names ,[]);
const ssB = SpreadsheetApp.openById(bookBId);
const fileFormatsFromBookB = []; // collect the values you find in each sheet
const submissionMethods = []; //collect all the submission methods in each sheet
for (const sheetName of sheetNames) {
const sheet = ssB.getSheetByName(sheetName);
if (!sheet){
fileFormatsFromBookB.push(['Sheet Not Found'])
continue;
}
const pmsCell = sheet.getRange(pmsRange).getValue();
var array1 = [{}];
var string2 = pmsCell;
array1 = string2.split(/[:\n]/);
var pms = array1[1];
pms = pms.replace(/\s+/g, '').toLowerCase();
console.log(sheetName)
console.log(pms)
if(pms==pmsOpera){
const fileFormatRange = sheet.getRange(operaRow, operaCol, sheet.getLastRow(), 1);
const fileFormats = fileFormatRange.getValues();
var col0 = fileFormats.map(function(value,index) { return value[0]; });
const distinct = (value, index, self) =>{ return self.indexOf(value)===index;}
var unq = col0.filter(distinct).toString();
fileFormatsFromBookB.push([unq]);
//ERROR SKIPS BELOW CODE//
const subMethodsRange = sheet.getRange(subRowOpr, subColOpr, sheet.getLastRow(), 1);
const subMethods = subMethodsRange.getValues();
var col1 = subMethods.map(function(value,index) { return value[0]; });
const distinct1 = (value, index, self) =>{ return self.indexOf(value)===index;}
var unqSub = col1.filter(distinct1).toString();
submissionMethods.push([unqSub]);
console.log(Error)
}
if (pms==pmsOnq || pms==pmsFosse || pms==pmsGalaxy) {
const fileFormatRange = sheet.getRange(fileFormatRow, fileFormatCol, sheet.getLastRow(), 1);
const fileFormats = fileFormatRange.getValues();
var col0 = fileFormats.map(function(value,index) { return value[0]; });
const distinct = (value, index, self) =>{ return self.indexOf(value)===index;}
var unq = col0.filter(distinct).toString();
fileFormatsFromBookB.push([unq]);
const subMethodsRange = sheet.getRange(subRow, subCol, sheet.getLastRow(), 1);
const subMethods = subMethodsRange.getValues();
var col1 = subMethods.map(function(value,index) { return value[0]; });
const distinct1 = (value, index, self) =>{ return self.indexOf(value)===index;}
var unqSub = col1.filter(distinct1).toString();
submissionMethods.push([unqSub]);
}
//else {
// submissionMethods.push(["__"]);
//}
sA.getRange(2, 10, fileFormatsFromBookB.length, 1).setValues(fileFormatsFromBookB); // paste all of the values you collected into the paste range you specified in book
sA.getRange(2, 11, submissionMethods.length, 1).setValues(submissionMethods); // paste submission methods:unique values
}
}
I managed to fix the error. It is actually a typo. I have not been pushing a value to the search array if a the search returns nothing. Code is below.
if (!sheet){
fileFormatsFromBookB.push(['Sheet Not Found'])
submissionMethods.push(['Sheet Not Found'])//THIS FIXED THE ISSUE
continue;
}
Thank you for all the help guys.

Merge the following regular function and onEdit function to obtain the desired result

I want to merge these two functions below to get the desired output which is the following:
Lets assume, we have size variations available in Column E of Sheet 1. So, I want the function to copy each record in sheet 1 to sheet 2. But, each copied records gets copied multiple times depending on the number of size variations available (Col E); plus, one copy which will not contain the size variation just the product info. Basically, first copy of the record will just contain info such as product, color, purchase date. Following copies of this record will contain the size variations but not the purchase date.
For reference dummy sheet link: https://docs.google.com/spreadsheets/d/1_-978mgxiRrN5LcLFALtfrhMm_ULSy_jhS2kbYrGLNk/edit?usp=sharing
function myFunction() {
const ss = SpreadsheetApp.getActive();
const sh1 = ss.getSheetByName('Sheet 1');
const sh2 = ss.getSheetByName('Sheet 2');
const sizes = sh1.getRange('E2:E5').getValues().flat();
const vals1 = sh1.getRange('A2:D'+sh1.getLastRow()).getValues();
const values = vals1.filter(r=>r[0]==true).map(([,b,c,d])=>[b,c,d]);
const emptyAr = [...new Array(3)].map(elem => new Array(3));
const valuesAr = values.flatMap(r=>[r,...emptyAr]);
const sizesAr = new Array(values.length).fill(sizes).flat().map(c=>[c]);
const lrow = sh2.getLastRow();
sh2.getRange(lrow+1,2,valuesAr.length,valuesAr[0].length).setValues(valuesAr);
sh2.getRange(lrow+1,11,sizesAr.length,sizesAr[0].length).setValues(sizesAr);
}
function onEdit(e) {
const rng = e.range;
const row = rng.getRow();
const col = rng.getColumn();
const sh = rng.getSheet();
if(sh.getName()=="Sheet 1" && col == 1 && e.value=="TRUE") {
const tsh=e.source.getSheetByName("Sheet 2");
const nr=tsh.getLastRow()+1;
const size = sh.getRange("E2:E").getValues().filter(String);
const len = size.length;
const product = new Array(len).fill([sh.getRange(row,2).getValue()]);
const color = new Array(len).fill([sh.getRange(row,4).getValue()]);
tsh.getRange(nr,2,product.length,1).setValues(product);
tsh.getRange(nr,4,color.length,1).setValues(color);
tsh.getRange(nr,11,size.length,1).setValues(size);
}
}
Explanation:
You want to copy the product and color based on the number of the size elements.
Replace:
const valuesAr = values.flatMap(r=>[r,...emptyAr]);
with:
const valuesAr = values.flatMap(r=>[...new Array(sizes.length)].map(elem => r));
and you can now delete emptyAr and you can also get rid of the onEdit trigger if you don't need it.
Solution:
function myFunction() {
const ss = SpreadsheetApp.getActive();
const sh1 = ss.getSheetByName('Sheet 1');
const sh2 = ss.getSheetByName('Sheet 2');
const sizes = sh1.getRange('E2:E5').getValues().flat();
const vals1 = sh1.getRange('A2:D'+sh1.getLastRow()).getValues();
const values = vals1.filter(r=>r[0]==true).map(([,b,c,d])=>[b,c,d]);
const valuesAr = values.flatMap(r=>[...new Array(sizes.length)].map(elem => r));
const sizesAr = new Array(values.length).fill(sizes).flat().map(c=>[c]);
const lrow = sh2.getLastRow();
sh2.getRange(lrow+1,2,valuesAr.length,valuesAr[0].length).setValues(valuesAr);
sh2.getRange(lrow+1,11,sizesAr.length,sizesAr[0].length).setValues(sizesAr);
}

SetValues() to two different cell ranges together in Google Sheet

I have two spreadsheet books.
BookA
BookB
I have sheet names of BookB stored in BookA.
I want to search through all the sheets in BookB that matches the sheet name stored in BookA. If a sheet is found get the values in Cell 'A3' and paste it in BookA in front of the respective sheet name.
(I have managed to achieve this task successfully. Issue comes now. Brace yourselves)
I want to get the 'File Format' details without duplicates from the sheets of BookB and paste that in the sheet of BookA in front of the page name. May be my way is not correct. If someone can help I am grateful.
Note that File Format details are mentioned in two different ranges in the given two sheets. ALBW - D6:D21 and BFLCB - F6:F21
const pmsRange = 'A3' // the cell in book B sheet i that you want to copy
function getFileFormat(){
const ssA = SpreadsheetApp.openById(bookAId);
const sA = ssA.getSheetByName(sheetA);
const sheetNames = sA.getRange('G2:G').getValues().reduce((names, row) => row[0] !== '' ? names.concat(row[0]) : names ,[]);
const ssB = SpreadsheetApp.openById(bookBId);
const valuesFromSheetB = []; // collect the values you find in each sheet of book B
for (const sheetName of sheetNames) {
const sheet = ssB.getSheetByName(sheetName);
if (!sheet) {
valuesFromSheetB.push(['Sheet Not Found']);
continue;
}
const value = sheet.getRange(pmsRange).getValue(); // get the value from the range you specified
var array1 = [{}];
var string1 = value;
array1 = string1.split(/[:\n]/);
var pms = array1[1];
pms = pms.replace(/\s+/g, '');
if(pms.toLowerCase()=="onq"){
console.log(sheetName+":"+pms);
var col0 = exts.map(function(value,index) { return value[0]; });
const distinct = (value, index, self) =>{ return self.indexOf(value)===index;}
var unq = col0.filter(distinct).toString();
console.log(unq)
extsFromSheetB.push([unq])
}
sA.getRange(2, 8, valuesFromSheetB.length, 1).setValues(valuesFromSheetB); // paste all of the values you collected into the paste range you specified in book A
}
}
These edits should get you what you need. The script will find sheets in book B whose names are listed in book A. Once a sheet is found, it will check to see if the value in the pmsRange of that sheet contains the pmsSearchValue. If it does, then it will store all of the file formats separated by ' / '. If it doesn't then it will store ''. Finally, after iterating over every sheet name collected from book A, it will paste the file formats into the paste range that you specified in your example.
const pmsRange = 'A3' // the cell in book B sheet i that you want to copy
const pmsSearchValue = 'OnQ';
const fileFormatCol = 4 // column D
const fileFormatRow = 6 // first row containing file formats
function getFileFormat(){
const ssA = SpreadsheetApp.openById(bookAId);
const sA = ssA.getSheetByName(sheetA);
const sheetNames = sA.getRange('G2:G').getValues().reduce((names, row) => row[0] !== '' ? names.concat(row[0]) : names ,[]);
const ssB = SpreadsheetApp.openById(bookBId);
const fileFormatsFromBookB = []; // collect the values you find in each sheet of book B
for (const sheetName of sheetNames) {
const sheet = ssB.getSheetByName(sheetName);
if (!sheet) continue;
const pmsCell = sheet.getRange(pmsRange).getValue();
if (pmsCell && pmsCell.indexOf(pmsSearchValue)) {
const fileFormatRange = sheet.getRange(fileFormatRow, fileFormatCol, sheet.getLastRow(), 1);
const fileFormats = fileFormatRange.getValues().filter(f => f !== '').join(' / ');
fileFormatsFromBookB.push([fileFormats]);
} else {
fileFormatsFromBookB.push(['']);
}
sA.getRange(2, 10, fileFormatsFromBookB.length, 1).setValues(fileFormatsFromBookB); // paste all of the values you collected into the paste range you specified in book A
}
}
References: None. This is mostly vanilla javascript taking advantage of the Apps Script Spreadsheet Class that you are already using in the sample in your question.
I managed to get the code edited by #RayGun to to fit to my requirement. Thank you. Posting the code here if someone else face the same issue as me.
const pmsRange = 'A3' // the cell in book B sheet i that you want to copy - pms
const pmsOnq = 'onq';
const pmsFosse = 'fosse'
const pmsGalaxy = 'galaxylightspeed'
const pmsOpera = 'opera'
const fileFormatCol = 4 // column D
const fileFormatRow = 6 // first row containing file formats
const operaCol = 6 // column F
const operaRow = 6 // first row of opera file formats
function getFileFormat(){
const ssA = SpreadsheetApp.openById(bookAId);
const sA = ssA.getSheetByName(sheetA);
const sheetNames = sA.getRange('G2:G').getValues().reduce((names, row) => row[0] !== '' ? names.concat(row[0]) : names ,[]);
const ssB = SpreadsheetApp.openById(bookBId);
const fileFormatsFromBookB = []; // collect the values you find in each sheet of book B
for (const sheetName of sheetNames) {
const sheet = ssB.getSheetByName(sheetName);
if (!sheet){
fileFormatsFromBookB.push(['Sheet Not Found'])
continue;
}
const pmsCell = sheet.getRange(pmsRange).getValue();
var array1 = [{}];
var string2 = pmsCell;
array1 = string2.split(/[:\n]/);
var pms = array1[1];
pms = pms.replace(/\s+/g, '').toLowerCase();
console.log(sheetName)
console.log(pms)
if (pms==pmsOnq || pms==pmsFosse || pms==pmsGalaxy) {
const fileFormatRange = sheet.getRange(fileFormatRow, fileFormatCol, sheet.getLastRow(), 1);
const fileFormats = fileFormatRange.getValues();
var col0 = fileFormats.map(function(value,index) { return value[0]; });
const distinct = (value, index, self) =>{ return self.indexOf(value)===index;}
var unq = col0.filter(distinct).toString();
fileFormatsFromBookB.push([unq]);
}
if(pms==pmsOpera){
const fileFormatRange = sheet.getRange(operaRow, operaCol, sheet.getLastRow(), 1);
const fileFormats = fileFormatRange.getValues();
var col0 = fileFormats.map(function(value,index) { return value[0]; });
const distinct = (value, index, self) =>{ return self.indexOf(value)===index;}
var unq = col0.filter(distinct).toString();
fileFormatsFromBookB.push([unq]);
}
/*else {
fileFormatsFromBookB.push(['']);
}*/
sA.getRange(2, 10, fileFormatsFromBookB.length, 1).setValues(fileFormatsFromBookB); // paste all of the values you collected into the paste range you specified in book
}
}
Note that you have to remove that else part of if statement. Otherwise it skips a cell and gives a result in a wrong range.

Google Script IF Statement

Can someone help me to convert the below condition to Google Script?
I have Column A and Column B and I need to get result in Column C.
Condition:
If A="Good" and B="5star" then C="OK"
If A="Fair" and B="5star" then C="OK"
If A!="Fair" or A!= "Good" and B="1star" then C="OK"
all other cases should be "NOK"
Note: != means not equal I mean.
You can use regular if/else if statements but I think ternary operators are cleaner:
function myFunction(){
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getSheetByName("Sheet1");
const vals = sh.getRange("A2:B"+sh.getLastRow()).getValues();
const cvals = [];
vals.forEach(r=>{
cvals.push( [
["Good","Fair"].includes(r[0]) && r[1]=="5star" ||
!["Good","Fair"].includes(r[0]) && r[1]=="1star"?"OK":"NOK"
])
});
sh.getRange(2,3,cvals.length,1).setValues(cvals);
}
With an if condition, it would be:
function myFunction(){
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getSheetByName("Sheet1");
const vals = sh.getRange("A2:B"+sh.getLastRow()).getValues();
const cvals = [];
vals.forEach(r=>{
if(["Good","Fair"].includes(r[0]) && r[1]=="5star" ||
!["Good","Fair"].includes(r[0]) && r[1]=="1star")
{cvals.push(["OK"])}
else{cvals.push(["NOK"])};
});
sh.getRange(2,3,cvals.length,1).setValues(cvals);
}
And here is how you can get the columns separately in case you have a non-contiguous range:
function myFunction(){
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getSheetByName("Sheet1");
const col1 = sh.getRange("A2:A"+sh.getLastRow()).getValues().flat();
const col2 = sh.getRange("B2:B"+sh.getLastRow()).getValues().flat();
const cvals = [];
col1.forEach((_,i)=>{
cvals.push( [
["Good","Fair"].includes(col1[i]) && col2[i]=="5star" ||
!["Good","Fair"].includes(col1[i]) && col2[i]=="1star"?"OK":"NOK"
])
});
sh.getRange(2,3,cvals.length,1).setValues(cvals); // 2 means second row, 3 means column C
}

Categories

Resources