Merge Data From Multiple Sheets Onto 1 Sheet - google-apps-script

I'm in the process of creating a script that'll merge data from multiple Google sheets onto one.
As I'm new to this, I'm having trouble figuring out an error I keep getting.
It states 'Exception: Range not found' in line 26 of the script.
I'm assuming, maybe incorrectly that there's something wrong with the range I'm using in my variables as to which data to merge onto the main sheet. But changing that doesn't seem to help.
Any tips, advice, or things to try is greatly appreciated.
Here's a link to the sheets.
https://docs.google.com/spreadsheets/d/1hv9zANMWzDBpKDWQ3hbDjX4nezEtOoptFbY0PYn_c5Y/edit?usp=sharing
Here's the code for the script.
function
myFunction() {
//FILTER({'Workout1'}) !E15:AA26,"DATA TAB 1 - ROW "&ROW('Workout1'!E15:E26)},'Workout1'!E15:E26<>"");
//FILTER({'Workout2'}) !E15:AA26,"DATA TAB 2 - ROW "&ROW('Workout2'!E15:E26)},'Workout2'!E15:E26<>"");
//FILTER({'Workout3'}) !E15:AA26,"DATA TAB 3 - ROW "&ROW('Workout3'!E15:E26)},'Workout3'!E15:E26"");
//FILTER({'Workout1'}) !E15:AA26,"DATA TAB 1 - ROW "&ROW('Workout1'!E15:E26)},'Workout1'!E15:E26<>"");
//setvariables
const masterSheet = "Data";
const ignoreSheets = ["Dashboard"];
const dataRange = "E15:AA26";
const checkRange = "E15:E26";
//end set variables
const ss = SpreadsheetApp.getActiveSpreadsheet();
const allsheets = ss.getSheets();
const filteredlistofsheets = allsheets.filter(s => ignoreSheets.indexOf(s.getSheetName()) == -1);
let formulaArray = filteredlistofsheets.map(s => `FILTER({'${s.getSheetName()}'$!{datarange},"${s.getSheetName()} - ROW "&ROW('${s.getSheetName()}!${checkRange},ROW "&ROW('${s.getSheetName()}'!${checkRange}<>"")`);
let formulaText = "={" + formulaArray.join(";") + "}";
ss.getSheetByName(masterSheet).getRange("masterSheetFormulaCell").setformula(formulaText);
}
Thanks very much.

You can do this without scripts using REDUCE.
=QUERY(
REDUCE({0,0,0,0},
{"Workout1",
"Workout2",
"Workout3"},
LAMBDA(acc,cur,
{acc;
INDIRECT(cur&"!E15:E26"),
BYROW(INDIRECT(cur&"!K15:O26"),LAMBDA(row,IFERROR(AVERAGE(row)))),
BYROW(INDIRECT(cur&"!Q15:U26"),LAMBDA(row,IFERROR(AVERAGE(row)))),
BYROW(INDIRECT(cur&"!W15:AA26"),LAMBDA(row,IFERROR(AVERAGE(row))))})
),
"select Col1,
sum(Col2),
sum(Col3),
avg(Col4)
where Col1 is not null
group by Col1
label Col1 'Exercise',
sum(Col2) 'Reps For Each Set',
sum(Col3) 'Weight Per Set (lbs.)',
avg(Col4) 'Rest Between Sets (sec.)'",1)

function mergeAllToFirst() {
const ss = SpreadsheetApp.openById("Spreadsheet ID");
let sht;
ss.getSheets().forEach((sh, i) => {
if (i == 0) {
sh.clearContents()
sht = sh;
} else {
let vs = sh.getDataRange().getValues();
sht.getRange(sht.getLastRow() + 1, 1).setValue(sh.getName());
sht.getRange(sht.getLastRow() + 1,1,vs.length,vs[0].length).setValues(vs);
}
})
}

Related

Question on comparing 2 column values and setting value of an adjacent column to another sheet

I am trying to compare values on 2 different columns values to ensure that they exist(on Sheet 1)
Once the script is aware that they exist I need to access sheet1's column for quantity and add that value to sheet 2's quantity column. The problem is I am unsure of how to just get the location/index of the foreach loop and offset a setValue to another column without setting the value to the entire column(I dont want to do that if the product name of column A does not exist in Sheet1)
Here is the code example of how i am trying to do it
I have included it in a pastebin because I could not figure out how to format the code to paste ( sorry i'm super new at this!)
<https://pastebin.com/EKB2n9kA>
Sheet1 incoming data https://drive.google.com/file/d/1eLNeOZZbdeCDfMMImksVRnBXwKxpHIO_/view?usp=sharing
Sheet2 'base' data to add quantity values to https://drive.google.com/file/d/1h26H9eQgZapd2Y0LVamhRPYme-8LmVF0/view?usp=sharing
example of expected/wanted results https://drive.google.com/file/d/1-0ozD5PrbIq-otG4j7kAyLufQFjDR5Hi/view?usp=sharing
I have also attached 3 different reference photos
Sheet 1 is 'incoming' data to read
Sheet 2 is our 'base' data and where Sheet1's quantity column needs to be added to
the third screenshot is the expected result(having the script skip over rows that do not contain matching data but still being able to get the quantity value based on the index/location the value was found)
Any insight on how to achieve this would be sincerely appreciated
I have tried pushing the results into an empty array but it does not seem to give much useful info the way I am doing it.
I have also tried just getting an offset range (getRange("G2:G").offset(0,3).setValues() to set the results but it sets the value of the entire column instead of only where the values match for each column being compared.
From your <https://pastebin.com/EKB2n9kA>, it seems that your current script is as follows.
function compareCol() {
var ss = SpreadsheetApp.getActiveSpreadSheet();
var s1 = ss.getSheetByName("Sheet1");
var s2 = ss.getSheetByName("Sheet2");
var datA = s1.getRange("A2:A").getValues();
var datB = s2.getRange("A2:A").getValues();
var quantityA = s1.getRange("C2:C").getValues();
var quantityB = s2.getRange("D2:D");
let matchingCol = [];
for (var i in datA) {
for (var x in datB[i]) {
if (datA[i][0] === datB[x][0]) {
}
}
}
}
Modification points:
In your script, the if statement in the loop has no script. And, quantityA, quantityB, and matchingCol are not used.
When your script is modified for achieving your goal, how about the following modification?
Modified script:
function compareCol() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s1 = ss.getSheetByName("Sheet1");
var s2 = ss.getSheetByName("Sheet2");
var datA = s1.getRange("A2:C" + s1.getLastRow()).getValues().reduce((o, [a, , c]) => (o[a] = c, o), {});
var datB = s2.getRange("A2:D" + s2.getLastRow()).getValues();
var matchingCol = datB.map(([a, , , d]) => [datA[a] || d]);
s2.getRange(2, 4, matchingCol.length).setValues(matchingCol);
}
In this modification, when the value of column "A" in "Sheet2" is not found from column "A" of "Sheet1", the value of column "D" of "Sheet2" is used. If you want to remove this, please modify it as follows.
From
var matchingCol = datB.map(([a, , , d]) => [datA[a] || d]);
To
var matchingCol = datB.map(([a]) => [datA[a] || null]);
References:
reduce()
map()
Hopefully this will help
function comparingColumns() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const osh = ss.getSheetByName("Sheet1");
const [h,...vs] = sh.getDataRange().getValues();
vs.forEach((r, i) => {
//comparing column A to Column D
if (r[0] == r[3]) {
//getting column G of same row in sheet1
osh.getRange(i + 2, 7).setValue(r[6]);
}
})
}

fixing my #ERROR in google sheets with google apps script

I have a google app script program that looks throughout a spreadsheet and moves rows with empty values in column 5 into another sheet (block 3 of code).
However, I keep getting a #error upon transfer because in certain rows, some phone numbers have a + in front of them. I tried to turn the entire number into a string (block 2 of code with foreach loop), but the error remains.
function insertRecord() {
var sheet_2 = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
var sheet_1 = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var rows = sheet_1.getDataRange().getValues();
rows.forEach(function(element) {
iterator=element.length;
for (i=0;i<iterator+1;i++){
if (typeof element[i]=== Number){
element[i]=element[i].toString
}
}
});
rows
.filter(row => row[5] == '')
.map(row_w_empty5col => sheet_2.appendRow(row_w_empty5col));
}
When I saw your script, at for (i=0;i<iterator+1;i++){, I thought that i<iterator+1 will be i<iterator. And, toString of element[i]=element[i].toString will be toString(). And also, when appendRow is used in a loop, the process cost will be high. Ref
And, about moves rows with empty values in column 5 into another sheet, in your script, .filter(row => row[5] == '') is used. In this case, the column 6 is checked. In order to check the column 5 (column "E"), it is row[4].
From your current issue and your goal, in your situation, how about copying the number format? When this is reflected in your script, it becomes as follows.
Modified script:
function insertRecord() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet_2 = ss.getSheets()[1];
var sheet_1 = ss.getSheets()[0];
var range = sheet_1.getDataRange();
var rows = range.getValues();
var f = range.getNumberFormats();
var n = rows.reduce((ar, r, i) => {
if (r[4].toString() == "") ar.push(i);
return ar;
}, []);
var values = n.map(e => rows[e]);
var formats = n.map(e => f[e]);
sheet_2.getRange(sheet_2.getLastRow() + 1, 1, values.length, values[0].length).setValues(values).setNumberFormats(formats);
}
References:
getNumberFormats()
setNumberFormats(numberFormats)
Update: Changed my code a little bit, and it works now.
function emailEditor() {
var app=SpreadsheetApp;
var targetSht=app.getActiveSpreadsheet().getSheetByName('Linkedin Only');
var spreadsheet=app.getActiveSpreadsheet().getSheets().length;
for (i=0;i<spreadsheet;i++){
var shts=app.getActiveSpreadsheet().getSheets()[i];
var originalData=shts.getDataRange().getValues();
var newData=originalData.filter(function(item){
return item[5]===''; //user declared input here
});
var begin=targetSht.getDataRange().getValues().length;
targetSht.getRange(begin+1,1,newData.length,newData[0].length).setValues(newData);
}
}

FilterCriteria on Google Script to filter using checkboxes

Im currently trying to move in bulk all the rows from a sheet where the checkbox is selected, I was trying to use "newFilterCriteria" but the one I use (found online) check on specific words while I want to use checkboxes.
The code so far does:
Get the source page, declare the filter criteria
function create_filter(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Fridge"); // DECLARE THE SOURCE PAGE
var range = sheet1.getRange("A5:J"); // DECLARE THE RANGE TO BE FILTERED LATER
var filter = range.createFilter();
var Filter_Criteria1 = SpreadsheetApp.newFilterCriteria().withCriteria(true); // HERE IS THE PROBLEM, THE ORIGINAL CODE SAYS "newFilterCriteria().whenNumberGreaterThan(1000);" BUT INSTEAD OF A NUMBER, I NEED A FILTER BASED ON CHECKBOXES BEING EITHER TRUE OR FALSE
var add_filter1 = filter.setColumnFilterCriteria(1,Filter_Criteria1);
Logger.log("Filter has been added.");
var range = sheet1.getDataRange();
var new_sheet = ss.insertSheet(); // CREATE THE DESTINATION TAB
new_sheet.setName("TEST"); // NAME THE DESTINATION TAB AS "TEST"
range.copyTo(new_sheet.getRange(1,1));
filter.remove();
}
Any suggestions or help? Thank you! I tried looking around but havent get to find the right way to filter with the checkboxes.
Something else: Not sure if I can avoid an iteration since there are many rows to copy and it would be a slow process I think, is there a way to say something like a query such as "select all rows where column 1 is true"?
The image is just an example of the table.
Thanks!
Move Checked
function myfunk() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const vs = sh.getRange(2, 1, sh.getLastRow() - 1, sh.getLastColumn()).getValues();
let a = [];
let d = 0;
vs.forEach((r, i) => {
if (r[0] == true) {
a.push(r)
sh.deleteRow(i + 2 - d++);
}
});
if (a) {
ss.insertSheet('Test').getRange(1, 1, a.length, a[0].length).setValues(a);
}
}
Use whenTextEqualTo true, for filtering in checkboxes:
const Filter_Criteria1 = SpreadsheetApp.newFilterCriteria().whenTextEqualTo('TRUE').build()

Find and Copy Cell Information

I am attempting to write a script in Google Sheets that will read column D and if the cell contains, amongst others, the word "Apple" then it will copy the information from the same row in column B to a different sheet in cell(s) C28:C29.
Any help on starting the script would be greatly appreciated. Thanks!
function myfunc101() {
const sss = SpreadsheetApp.getActive();
const ssh = sss.getSheetByName('Sheet1');
const ssr = 2;
const srg = ssh.getRange(ssr,1,ssh.getLastRow() - ssr +1,4);
const svs = srg.getValues();
const tss = SpreadsheetApp.openById('tssid');
const tsh = tss.getSheetByName('Sheet1');
let c28 = [];
let c29 = [];
//let d = 0;//add this to delete current row of ssh
svs.forEach((r,i)=>{
if(r[3].toString().includes('Apple')) {
c28.push(r[1]);
c29.push(r[3]);
//sh.deleteRow(i+ssr-d++);//add this to delete current row of ssh
}
});
tsh.getRange('C28').setValue(c28.join(', '));
tsh.getRange('C29').setValue(c29.join(', '));
}
if C28 and C29 are merged and you want all of the data in C28 then replace last two lines with: tsh.getRange('C28').setValue(tsh.getRange('C28').getDisplayValue() + c28.join(', ') + c29.join(', '));

Is there a way to iterate down rows and across columns and compile that data into a cell?

I am trying to compile data from one sheet containing order information to another sheets cell based on each customers name. I'd like to take the title of the column as well as the count for the item(s) they have ordered. I tried to brute force it with a formula in Google Sheets but the formula messes up and stops giving the correct information so I figured using a scripts function would be better for what I am trying to do. I made a new sheet to test a script on but have little experience and can't seem to make any progress.
I'd like to get the title(top row) of each column and the count of the item(s) into the order column on sheet2 base on the matching names on both sheets. If anyone could help or provide some insight it would be greatly appreciated.
Here is the code I came up with:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var orders = ss.getSheetByName("Sheet2");
var data = ss.getSheetByName("Sheet1");
var names = orders.getRange("Sheet2!A2:A5");
var name = names.getValues();
var startRow = 1
var endRow = ss.getLastRow()
var getRange = ss.getDataRange();
var getRow = getRange.getRow();
var title = data.getRange("Sheet1!B1:J1");
var item = title.getValues();
var itemCell = data.getRange("Sheet1!B2").getValue();
var orderCell = orders.getRange(2,2).getValue()
Logger.log(itemCell)
if(itemCell>=0){
orders.getRange(2,2).setValue("item1 x " +itemCell)
}
currently this only has the desired effect on one cell and does not complete the row and repeat on next column.
Here is how I adjusted the code to try and fit a larger data set:
function myFunction() {
const srcSheetName = "Test Data";
const dstSheetName = "Order Changes";
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 1. Retrieve source values.
const srcSheet = ss.getSheetByName(srcSheetName);
//I changed values 1,1 to change the values that were retreived
const [[, ...header], ...srcValues] = srcSheet.getRange(1, 1,
srcSheet.getLastRow(), srcSheet.getLastColumn()).getValues();
// 2. Create an object using the source values.
const srcObj = srcValues.reduce((o, [a, ...v]) => {
const temp = v.reduce((s, r, i) => {
if (r.toString() != "") s += `${header[i]} ${r}`;
return s;
}, "");
return Object.assign(o, {[a]: temp || ""});
}, {});
// 3. Retrieve the header column of destination values.
const dstSheet = ss.getSheetByName(dstSheetName);
//I changed values 2 or 1 to adjust the destination of values
const dstRange = dstSheet.getRange(2, 1, dstSheet.getLastRow() - 1);
const dstValues = dstRange.getValues();
// 4. Create the output values using the header column and the object.
const putValues = dstValues.map(([a]) => [srcObj[a] || ""]);
// 5. Put the values.
dstRange.offset(0, 1).setValues(putValues);
}
after making the changes and running the code the values would either not appear or appear in the wrong column with incorrect data.
Goal of function Update:
Match names in Sheet2!A with names in Sheet1!F
If a match is found combine header and value of cells in Sheet1!N1:BQ.
This should be from the same row of the matched name in Sheet1!F (example:
John Smith lm14 1, lm25 2, lm36 1)
Place combined data into Sheet2!C
Repeat for every name in Sheet2!A
header and cell value should not be combined if value < 0
I hope this helps to clarify any misunderstanding.
Here is are better example images:
I believe your goal as follows.
In your goal, the upper image in your question is the output you expect, and the cells "B2" should be item1 1 item3 1 item6 2 item9 3 when the lower input situation is used.
You want to achieve this using Google Apps Script.
In order to achieve above, I would like to propose the following flow.
Retrieve source values.
Create an object using the source values.
Retrieve the header column of destination values.
Create the output values using the header column and the object.
Put the values.
Sample script:
Please copy and paste the following script to the script editor of the Spreadsheet and set the source sheet name and destination sheet name, and run myFunction.
function myFunction() {
const srcSheetName = "Sheet1";
const dstSheetName = "Sheet2";
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 1. Retrieve source values.
const srcSheet = ss.getSheetByName(srcSheetName);
const [[, ...header], ...srcValues] = srcSheet.getRange(1, 1, srcSheet.getLastRow(), srcSheet.getLastColumn()).getValues();
// 2. Create an object using the source values.
const srcObj = srcValues.reduce((o, [a, ...v]) => {
const temp = v.reduce((s, r, i) => {
if (r.toString() != "") s += `${header[i]} ${r}`;
return s;
}, "");
return Object.assign(o, {[a]: temp || ""});
}, {});
// 3. Retrieve the header column of destination values.
const dstSheet = ss.getSheetByName(dstSheetName);
const dstRange = dstSheet.getRange(2, 1, dstSheet.getLastRow() - 1);
const dstValues = dstRange.getValues();
// 4. Create the output values using the header column and the object.
const putValues = dstValues.map(([a]) => [srcObj[a] || ""]);
// 5. Put the values.
dstRange.offset(0, 1).setValues(putValues);
}
References:
getValues()
setValues(values)
reduce()
map()
Added 1:
About your current issue, the reason of your issue is that the ranges of source and destination are the different from the sample image in your question. My suggested answer is for the sample images in your initial question. When you changed the structure of the Spreadsheet, it is required to modify my suggested script. But from I changed values 1,1 to change the values that were retrieved and I changed values 2 or 1 to adjust the destination of values, I couldn't understand about your modified script. So as the additional script, I would like to modify my suggested answer for your updated question.
From your updated question and replyings, I understood that the source range and destination range are "N1:BQ" and "A52:C79", respectively. From this, please modify above sample script as follows.
From:
const [[, ...header], ...srcValues] = srcSheet.getRange(1, 1, srcSheet.getLastRow(), srcSheet.getLastColumn()).getValues();
To:
const [[, ...header], ...srcValues] = srcSheet.getRange("N1:BQ" + srcSheet.getLastRow()).getValues();
and
From:
dstRange.offset(0, 1).setValues(putValues);
To:
dstRange.offset(0, 2).setValues(putValues);
Added 2:
About your current issue, the reason of your issue is that the ranges of source and destination are the different from your 1st updated question in your question. My suggested answer is for the sample images in your 1st updated question. When you changed the structure of the Spreadsheet, it is required to modify my suggested script.
From your 2nd updated question, I understood that the source range and destination range are "F1:BQ" (the column "F" is the title and the columns "N1:BQ" are the values.) and "A2:C", respectively. From this, please modify above sample script as follows.
function myFunction() {
const srcSheetName = "Sheet1";
const dstSheetName = "Sheet2";
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 1. Retrieve source values.
const srcSheet = ss.getSheetByName(srcSheetName);
const [[,,,,,,,, ...header], ...srcValues] = srcSheet.getRange("F1:BQ" + srcSheet.getLastRow()).getValues();
// 2. Create an object using the source values.
const srcObj = srcValues.reduce((o, [a,,,,,,,, ...v]) => {
const temp = v.reduce((s, r, i) => {
if (r.toString() != "") s += `${header[i]} ${r}`;
return s;
}, "");
return Object.assign(o, {[a]: temp || ""});
}, {});
// 3. Retrieve the header column of destination values.
const dstSheet = ss.getSheetByName(dstSheetName);
const dstRange = dstSheet.getRange(2, 1, dstSheet.getLastRow() - 1);
const dstValues = dstRange.getValues();
// 4. Create the output values using the header column and the object.
const putValues = dstValues.map(([a]) => [srcObj[a] || ""]);
console.log(srcObj)
// 5. Put the values.
dstRange.offset(0, 2).setValues(putValues);
}