google apps script two columns summary - google-apps-script

I have a google spreadsheet with two columns corresponding to lessons: the first with names of the porfessors (occasionally repeating themselves) and the second with numbers (number of hours). I would like to have as output two columns, the first with the names of the porfessors and the second with the sum of all the hours
I tried with the following code, but it seems to give me back two arrays with the initial colums, as if the condition if (names[names.length-1] == namesColumn[i]) is never met.
What am I doing wrong?
function resumeProfessors() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];
var namesColumn = sheet.getRange("C4:C31").getValues();
var lessonsColumn = sheet.getRange("G4:G31").getValues();
var names = [];
names.length = 0;
var lessons = [];
lessons.length = 0;
namesColumn.sort();
for (var i = 0; i < namesColumn.length; i++) {
if (names[names.length-1] == namesColumn[i]){
lessons[lessons.length-1] = lessons[lessons.length-1] + lessonsColumn[i];}
else{
sheet.getRange(i+4, 9).setValue(names[names.length-1] + namesColumn[i]);
names[names.length] = namesColumn[i];
lessons[lessons.length] = lessonsColumn[i];
};}
writeResume(names, lessons);
}
Ty

Given your use-case, I'd recommend using a Pivot table or the =QUERY formula.
However, assuming your input sheet looks something like this -
And the expected output is something like this -
You can try the below code -
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var input = ss.getSheetByName('Sheet1');
var output = ss.getSheetByName('Sheet2');
var inputValues = input.getDataRange().getValues();
Logger.log(inputValues)
for (var i = 1; i < inputValues.length; i++) {
var name = inputValues[i][0];
var totalHours = [];
for (var j = 0; j < inputValues.length; j++) {
var hours = inputValues[j][1];
if (name == inputValues[j][0]) {
totalHours.push(inputValues[j][1]);
}
}
var outputValues = output.getDataRange().getValues();
var newEntry = true;
for (var k = 0; k < outputValues.length; k++) {
if (name == outputValues[k][0]) {
newEntry = false;
}
}
if (newEntry) {
output.appendRow([name,totalHours.reduce(function(a, b) {return a + b})]);
}
}
}
Hope this helps.

Related

How to loop through cells in Google Sheets and log information if specific variables exist

You will need this link to solve this: - https://docs.google.com/spreadsheets/d/11PjVSWPfqOBPSej3AlQ-_T8Bws66kevR08Q2NRTmVcE/edit?usp=sharing
So this is a tricky one. I am looking to loop through the name in Sheet1 Column A, and also loop through the type in Column C. Then if Column A in Sheet1 matches the name of column A in Sheet2, AND the type is "pickup" I want to log the other information that is in Sheet 1 inside of Column C on Sheet 2. Log the things like "State", "Location", "City" next to the identical name in Sheet 2. I hope that makes sense.
I know I probably need to use a loop within a loop to do all this, but this is as far as I can get as I cannot figure out how to write the loop and then log the rows where the 2 names match.
function myFunction() {
function pullDeliveryNotes()
{
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
var dataRange = sheet.getDataRange();
var dataRange2 = sheet2.getDataRange();
var rng = sheet.getRange(2,1, dataRange.getLastRow()-1,dataRange.getLastColumn());
var rng2 = sheet.getRange(2,3, dataRange.getLastRow()-1,dataRange.getLastColumn());
var rng3 = sheet2.getRange(2,1, dataRange2.getLastRow()-1,dataRange2.getLastColumn());
var rngA = rng.getValues().toString()
var rngB = rng2.getValues().toString()
var rngC = rng3.getValues().toString()
for(var i = 0; i < rng.length; i++) {
for (var x = 0; x < rng2.length; x++) {
for (var y = 0; y < rng3.length; y++) {
if (rng[i][x][y].includes(rng3[i][x][y]) && rng2[i][x][y] === "pickUp") {
Logger.log("We got it")
}
}
}
}
}
}
Based on your problem statement.
Try this sample script:-
function checkValues() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ssSource = ss.getSheetByName('Sheet1')
const ssTarget = ss.getSheetByName('Sheet2')
const sourceRange = ssSource.getDataRange().getValues().filter(r=> r[2] === 'Pickup'); // Filtering only pickup values
const targetRange = ssTarget.getDataRange().getValues();
for(var i = 0 ; i < sourceRange.length ; i++)
{
for(var j = 1 ; j < targetRange.length ; j++)
{
if(sourceRange[i][0] === targetRange[j][0]) // if name matches
{
ssTarget.getRange(`C${j+1}`).setValue(`${sourceRange[i][3]},${sourceRange[i][4]},${sourceRange[i][5]}`) // set values in column C
}
}
}
}

How to Change .getMergedRanges() From String to a Range?

The following code will run very slow in my Google Sheets because my getRange is too large. Is there a way to only loop through the columns that are merged? I only want the for loop to get the number of columns in "yourRange" that are merged.
function getUpfrontCosts() {
var sheet = SpreadsheetApp.getActive().getSheetByName('LPB_COST');
var cl , count=0;
var yourRange = sheet.getRange("H13:UV13");
for (var i = 1; i < yourRange.getNumColumns()+1; i++)
{
cl=yourRange.getCell(1, i);
if (cl.isPartOfMerge()){
if (cl.offset(15, 0).getBackground() == "#ff8300" && cl.getMergedRanges()[0].getCell(1, 1).getValue()=='Upfront Costs') {
count = count + cl.offset(15, 0).getValue();
}
else {
}
} else {
}
}
return count;
};
The second code is how I am trying to turn a string to a range. I am getting "Cell reference out of range" error
How can I change cl to not be a string and be a range?
function getUpfrontCosts()
{
var sheet = SpreadsheetApp.getActive().getSheetByName('LPB_COST');
var destSheet = SpreadsheetApp.getActive().getSheetByName('Top Level PN');
var cl , count=0;
var yourRange = sheet.getRange("I13:UZ13");
var mergedRanges = yourRange.getMergedRanges();
for (var i = 0; i < mergedRanges; i++){
}
var newRange = sheet.getRange(mergedRanges[i].getA1Notation());
Logger.log(newRange.getA1Notation());
for (var i = 0; i < newRange.getNumColumns()+1; i++){
cl=newRange.getCell(1, i);
Logger.log(newRange.getA1Notation());
if (cl.offset(15, 0).getBackground() == "#ff8300" && cl.getValue()=='Upfront Costs') {
count = count + cl.offset(15, 0).getValue();
}
else {
}
}
return count;
};
This is the line with the error
cl=newRange.getCell(1, i);
if you want to use merge cell range, you can do this:
function UntitledMacro1()
{
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var cl , count=0 ;
var yourRange = sheet.getRange("I13:UZ13");
var bb = yourRange.getMergedRanges();
for (a=bb[0].getColumn();a<bb[0].getLastColumn()+1;a++)
{
//Your actual columns from I13, for first merge range, here your cl,
//but if your range is ("13:13"), you don't need
//-sheet.getRange("I13").getColumn()+1
cl=yourRange.getCell(1, a-sheet.getRange("I13").getColumn()+1);
//For your offset 15
Logger.log(cl.offset(15, 0).getValue());
//on so on
}
};

Script: Transpose, Find and Save

I have some data containing, Dates, Usernames and an average percent that i want to save in a certain way. My problem is that the order of the usernames can change depending on if new ones are added. I therefore need to "find" a specific username and then save the percentage data in the correct column.
I have found some code that partially helps me save the data that i need. But i could use some help in the "find" the corresponding username and save it in a certain Column part.
function save() {
var sss = SpreadsheetApp.getActive();
var ss = sss.getSheetByName('Result');
var range = ss.getRange('B1:B10');
var data = range.getValues();
var tss = SpreadsheetApp.getActive();
var ts = tss.getSheetByName('Archive');
ts.getRange(ts.getLastRow()+1, 1,data[0].length,data.length)
.setValues(Object.keys(data[0]).map ( function (columnNumber) {
return data.map( function (row) {
return row[columnNumber];
});
}));
}
Basically from this:
To a result that looks like this:
Thank you for your assistance.
Alright for anyone out there that may have a similar problem, this is what i ended up with.
function extractAttendance() {
var currentSheet = SpreadsheetApp.getActive();
var attendanceTab = currentSheet.getSheetByName('Data_Filtered');
var userData = attendanceTab.getRange('B1:B').getValues();
var percentageData = attendanceTab.getRange('I1:I').getValues();
var archiveTab = currentSheet.getSheetByName('Archive');
var existingUsersRow = archiveTab.getRange('1:1');
var newRowNumber = archiveTab.getLastRow() + 1;
archiveTab.getRange(newRowNumber, 1).setValue(new Date());
for (var i = 1; i < userData.length; i++) {
var user = userData[i][0];
if (user === '') {
continue;
}
var existingUsers = existingUsersRow.getValues()[0];
var exists = false;
var existingColumnNumber = -1;
for (var j = 0; j < existingUsers.length; j++) {
if (existingUsers[j] === user) {
exists = true;
existingColumnNumber = j + 1;
break;
}
}
if (exists) {
archiveTab.getRange(newRowNumber, existingColumnNumber).setValue(percentageData[i]);
} else {
var newColumnNumber = archiveTab.getLastColumn() + 1;
archiveTab.getRange(1, newColumnNumber).setValue(user);
archiveTab.getRange(newRowNumber, newColumnNumber).setValue(percentageData[i]);
}
}
}
It might be easier to implement your desired functionality through looping rather than through mapping.
The following code retrieves all users ad their percentage data in ‘Result’ and transfers the data (in the format you desire) to "Archive" with the percentages data pasted with the corresponding timestamp into the first empty row.
function save() {
var sss = SpreadsheetApp.getActive();
var ss = sss.getSheetByName('Result');
var range = ss.getRange('B1:B');
var percentageRange = ss.getRange('G1:G');
var userData = range.getValues();
var percentageData = percentageRange.getValues();
var tss = SpreadsheetApp.getActive();
var ts = tss.getSheetByName('Archive');
var userRow=1;
var percentageRow=(ts.getLastRow()+1)
for(var i=0; i<=userData.length; i++)
{
{
var j=(i+2);
ts.getRange(userRow, j).setValue(userData[i])
ts.getRange(percentageRow, 1).setValue(new Date())
ts.getRange(percentageRow, j).setValue(percentageData[i])
}
}
}

How to pass data from one column to another one ?

I have a spreadsheet with multiple rows and columns. Two columns (column 3&4) are filled with text. I want to clean the text from this two columns and delete every specific characters (newlines, comma, exclamation point, quote,etc...). So I wrote the following script :
function testwoD() {
var input = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Raw_data");
var output = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Raw_data");
var row_count = input.getLastRow()
var col_count = input.getLastColumn();
raw_data = input.getRange(1, 1,row_count,col_count).getValues()
temp3 = []
for (var i = 0; i < row_count; i++) {
var punctRE = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?#\[\]^_`{|}~\r\n|\n|\r]/g;
var spaceRE = /\s+/g;
temp3.push(raw_data[i][4].toString().replace(punctRE, '').replace(spaceRE, ' '));
}
temp4 = []
for (var i = 0; i < row_count; i++) {
var punctRE = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?#\[\]^_`{|}~\r\n|\n|\r]/g;
var spaceRE = /\s+/g;
temp4.push(raw_data[i][3].toString().replace(punctRE, '').replace(spaceRE, ' '));
}
var toAddArray3 = [];
for (i = 0; i < temp3.length; ++i){
toAddArray3.push([temp3[i]]);
}
var toAddArray4 = [];
for (i = 0; i < temp4.length; ++i){
toAddArray4.push([temp4[i]]);
}
output.getRange(1, col_count-13,row_count,1).setValues(toAddArray3);
output.getRange(1, col_count-14,row_count,1).setValues(toAddArray4);
}
It's working but It's very complicated and confusing. I made it step-by-step so even myself have some difficulties to really explain it.
Is there a way to significantly improve it ?
Best,
Simon.
DRY! - Do not repeat yourself.
Another popular idiom should be UMNF - Use map not for.
Putting everything into its own function encapsulates functionality and puts the focus on what you want to do with the data at each level rather than bookkeeping indices and subscripts.
function cleanColumns() {
var input = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var output = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var raw_data = input.getDataRange().getValues();
var columnsToClean = [3,4];
function cleanText(t) {
var punctRE = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?#\[\]^_`{|}~\r\n|\n|\r]/g;
var spaceRE = /\s+/g;
return t.toString().replace(punctRE, "").replace(spaceRE, " ");
};
function cleanColumn(col) {
return raw_data
.map(function(row) {return row[col];})
.map(cleanText)
.map(function(row) {return [row];})
};
function cleanAndWrite(col) {
var data = cleanColumn(col);
output.getRange(1, col + 1, data.length, 1).setValues(data);
}
columnsToClean.forEach(cleanAndWrite);
}

Delete Duplicate Row in Google Spreadsheet

I need this script to delete the row (within the Registration sheet) with the matching Registration Code to the Cancel Registration sheet's Registration code. As of now, this script only deletes a row if "sheetR.deleteRow(i);" is not inside "if (regCodeR === regCodeCR) {}". It doesn't delete the correct row either.
function rD() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetR = ss.getSheetByName("Registration");
var sheetCR = ss.getSheetByName("Cancel Registration")
var dataR = sheetR.getDataRange().getValues();
var dataCR = sheetCR.getDataRange().getValues();
var headerRow = 1;
for (var i = 1; i in dataR && i in dataCR; ++i) {
var rowR = dataR[i];
var rowCR = dataCR[i];
var duplicate = false;
var regCodeR = sheetR.getRange(headerRow + i, 10).getValues();
var regCodeCR = sheetCR.getRange(headerRow + i, 9).getValues();
if (rowR[9] === rowCR[8]) {
duplicate = true;
}
}
if (regCodeR === regCodeCR) {
sheetR.deleteRow(i);
}
}
I tried this code with simple data having one column with registration numbers. You can modify the main sheet range according to your data. Also make sure to change the sheet names.
Tried and tested the code below :
function deleteDup(){
var mainSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('regis');
var cancelSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('cancel');
var mainSheetValues = mainSheet.getRange(2, 1, mainSheet.getLastRow(),1).getValues();
var cancelSheetValues = cancelSheet.getRange(2, 1, cancelSheet.getLastRow(),1).getValues();
var k = 0;
var del = [];
// Getting the row index of matching values
for (i = 0; i < cancelSheetValues.length; i++)
{
var cancelValue = cancelSheetValues[i][0];
Logger.log(cancelValue);
for (j = 0; j < mainSheetValues.length; j++)
{
if(mainSheetValues[j][0] == cancelValue){
del[k] = j;
k++;
}
}
}
del.sort();
var count =0;
// deleting the values from the main sheet values array
for (i = 0; i < k; i++ )
{
mainSheetValues.splice((del[i] - count), 1);
count++;
}
var len = mainSheetValues.length;
// Update the sheet with new values
mainSheet.getRange(2, 1, mainSheet.getLastRow(),1).clearContent();
mainSheet.getRange(2, 1, len,1).setValues(mainSheetValues);
}
But instead you can also do this way:
Set a flag value in the registration sheet if it matches with the cancellation sheet.
Then loop through the registration sheet data and delete the matching flag row.
Hope that helps!