How to pass data from one column to another one ? - google-apps-script

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);
}

Related

Running script for multiple search terms found on different sheet

I'm trying to get a SCORESHEET to populate from a REPORTSHEET, using a REFERENCESHEET to collate search terms and destination cells.
The script I'm running is as below. The idea is that the script finds searchDate's in the REFERENCESHEET and uses them to locate data columns in the REPORTSHEET:
function superAuto() {
var report = SpreadsheetApp.openById('REPORTSHEET');
var reportData = report.getDataRange().getValues();
var reference = SpreadsheetApp.openById('REFERENCESHEET');
var referenceData = reference.getDataRange().getValues();
var scorecard = SpreadsheetApp.openById('SCORESHEET');
var scorecardData = scorecard.getDataRange().getValues();
var tExpenses = "Total Expenses";
for(n=0;n<referenceData.length;++n){
var searchDate = referenceData[n][0] ;
Logger.log (searchDate)
}
var column = columnfinder(searchDate);
for (var a = 0; a < referenceData.length; a++) {
var refRow = referenceData[a];
for (var i = 0; i < reportData.length; i++) {
var row = reportData[i];
if (row[0] == tExpenses && refRow[0] == searchDate) {
scorecard.getRange(refRow[5]).setValue(row[column]);
}
}
}
}
function columnfinder(find) {
var report = SpreadsheetApp.openById('REPORTSHEET');
var reportData = report.getDataRange().getValues();
var reference = SpreadsheetApp.openById('REFERENCESHEET');
var referenceData = reference.getDataRange().getValues();
for(var j=0, jLen=reportData.length; j<jLen; j++) {
for(var k=0, kLen=reportData[0].length; k<kLen; k++) {
if(find == reportData[j][k]) {
Logger.log(k);
return (k);}
}
}
}
Broadly speaking, the code works, as if I define searchDate as one of the terms I'm looking for (e.g. Jan-21) it all works fine. The issue is that it doesn't seem to be doing so when finding multiple search terms - and therefore populating multiple rows - as per:
for(n=0;n<referenceData.length;++n){
var searchDate = referenceData[n][0] ;
Logger.log (searchDate)
}
The log tells me that it's finding searchDate's in the REFERENCESHEET, but it's not able to run them through function columnfinder (I get no logs for the second logger).
I suspect the answer lay somewhere in an earlier great answer I received to an earlier version of this idea - How to return multiple column values for setValue - but I've not been able to make it fit. Any thoughts?
EDIT: Please find a sample REFERENCESHEET & REPORTSHEET for more info:
The log tells me that it's finding searchDate's in the REFERENCESHEET,
but it's not able to run them through function columnfinder (I get no
logs for the second logger)
You don't execute columnfinder inside the for loop.
Try this:
for(n=0;n<referenceData.length;++n){
var searchDate = referenceData[n][0] ;
Logger.log(searchDate);
columnfinder(searchDate); // modified code
}
and you will get both logs.
Sorry if I misunderstood your question.
You need to use have it assigned to array since you are returning possible multiple columns/dates:
function superAuto() {
var report = SpreadsheetApp.openById('REPORTSHEET');
var reportData = report.getDataRange().getValues();
var reference = SpreadsheetApp.openById('REFERENCESHEET');
var referenceData = reference.getDataRange().getValues();
var scorecard = SpreadsheetApp.openById('SCORESHEET');
var scorecardData = scorecard.getDataRange().getValues();
var tExpenses = "Total Expenses";
var searchDates = [];
for (n = 0; n < referenceData.length; ++n) {
searchDates.push(referenceData[n][0])
}
var columns = columnfinder(searchDates);
columns.forEach(function (column, index) {
referenceData.forEach(function (refRow) {
reportData.forEach(function (row) {
if (row[0] == tExpenses && refRow[0] == searchDates[index]) {
scorecard.getRange(refRow[5].toString()).setValue(row[column]);
}
});
});
});
}
function columnfinder(dates) {
var report = SpreadsheetApp.openById('REPORTSHEET');
var reportData = report.getDataRange().getValues();
var reference = SpreadsheetApp.openById('REFERENCESHEET');
var referenceData = reference.getDataRange().getValues();
var columns = [];
dates.forEach(function (date) {
reportData.forEach(function (row, i) {
row.forEach(function (col, i) {
if (date == reportData[i][j]) {
columns.push(reportData[i][j]);
}
});
});
});
return columns;
}
I changed some variables into proper variable names to avoid confusion.
Additionally, if it doesn't work, you might need to share a proper visualization of the data, or better yet, provide some sample sheet we can work on for us to be able to give you a better and tested answer.
Thanks Marios, that's twice in a week. Much appreciated.
Slight adaptation, in order for it to populate SCORECARD I needed to bring everything into the for loop, as below:
for(n=0;n<referenceData.length;++n){
var searchDate = referenceData[n][0] ;
Logger.log (searchDate)
var column = columnfinder(searchDate);
for (var a = 0; a < referenceData.length; a++) {
var refRow = referenceData[a];
for (var i = 0; i < reportData.length; i++) {
var row = reportData[i];
if (row[0] == tExpenses && refRow[0] == searchDate) {
scorecard.getRange(refRow[5]).setValue(row[column]);
}
}
}
}

Google Sheets Apps Script RichTextValueBuilder Parameters Don't Match method Signature

I am working to programmatically display a list of residents for a "know your neighbors map" community project. I want to take any number of first & last names for a residence and list them with last names in bold and leading any first names & numbers. The kludge code below or from the link is my first pass. I have tried various iterations to get the exception to go away, but I have been unsuccessful and I can't find anything concrete in the documentation or on the Googles.
Make a copy and run it yourself with this link: https://docs.google.com/spreadsheets/d/1rWHG5CKHvFzohTq9fSl8p8AjNcFjX0Tatior_hvOncE/copy
I'm getting the following error: "Exception: The parameters (SpreadsheetApp.RichTextValueBuilder) don't match the method signature for SpreadsheetApp.Range.setRichTextValue. (line 49, file "Code")"
Any ideas?
Kludge down here!
function main() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheetByName('Data');
var address_array = dataSheet.getRange('AddressData').getValues();
var bold = SpreadsheetApp.newTextStyle().setBold(true).build();
var resident_count = 1;
for (var i = 0; i < address_array.length; i++) {
var resident_array = address_array[i];
var resident_cell_range_name = 'Resident' + resident_count.toString();
var resident_cell = dataSheet.getRange(resident_cell_range_name);
var resident_text_array = [];
var resident_bold = [];
for (var j = 0; j < resident_array.length; j += 3) {
var last = resident_array[j];
var first = resident_array[j + 1];
var number = resident_array[j + 2];
if (last) {
var bold_start_stop = [];
var total = length_all(resident_text_array);
bold_start_stop.push(total);
bold_start_stop.push(total + last.length);
resident_bold.push(bold_start_stop);
resident_text_array.push(last)
}
if (first) {
if (number) {
var name_num = first + ' - ' + number;
resident_text_array.push(name_num)
}
else {
resident_text_array.push(first)
}
} else {
if (number) {
resident_text_array.push(number)
}
}
}
var value = SpreadsheetApp.newRichTextValue();
var resident_text = 'Some text';
// var resident_text = resident_text_array.join(String.fromCharCode(10));
// var resident_text = resident_text_array.join(' ');
value.setText(resident_text);
// for (start_stop of resident_bold) {
// value.setTextStyle(start_stop[0], start_stop[1], bold);
// }
value.build();
// resident_cell.setValue('text');
resident_cell.setRichTextValue(value);
Logger.log(resident_text_array);
resident_count += 1;
}
Logger.log(address_array);
}
function length_all(resident_text_array){
var total = 0;
for (each of resident_text_array) {
total = total + each.length;
}
return total;
}
The issue was using the build() command outside of the setRichTextValue() function.
Original that led to the exception:
value.build();
resident_cell.setRichTextValue(value);
Fixed:
resident_cell.setRichTextValue(value.build());
I'm not sure why, but that did it!

Split some strings and pass them in two different columns.

I've the following situation :
Sheet 1 (input) :
www.url1.com?somestuff
www.url2.com?somestuff
www.url3.com?somestuff
www.url4.com?somestuff
Sheet 2 (expected output):
Col1 Col2
www.url1.com ?somestuff
www.url2.com ?somestuff
www.url3.com ?somestuff
www.url4.com ?somestuff
Here is what I've done until now :
function testwoD() {
var input = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Raw_data");
var output = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet11");
var row_count = input.getLastRow()
var col_count = input.getLastColumn();
raw_data = input.getRange(1, 1,row_count,col_count).getValues()
tempArr = [] // or new Array
for (var i = 0; i < row_count; i++) {
tempArr.push(raw_data[i][6].split("?")[0]);
tempArr.push(raw_data[i][6].split("?")[1]);
}
var toAddArray = [];
for (i = 0; i < tempArr.length; ++i){
toAddArray.push([tempArr[i]]);
}
Logger.log(tempArr)
output.getRange(1, 1,730,1).setValues(toAddArray);
}
And here is the result I have on Sheet2 :
www.url1.com
?somestuff
www.url2.com
?somestuff
www.url3.com
?somestuff
www.url4.com
?somestuff
How can I reach the expected output ? I've read a lot of questions about transposing array but couldn't find the answer that could help me solve my issue.
Thanks !
Instead of push and concat you can do this
tempArr = [] // or new Array
for (var i = 0; i < row_count; i++) {
tempArr[i] = []
tempArr[i][0] = raw_data[i][6].split("?")[0];
tempArr[i][1] = raw_data[i][6].split("?")[1];
}
or better still you just push the whole split array.
for (var i = 0; i < row_count; i++) {
tempArr.push(raw_data[i][6].split("?"))
}
The basic idea is to get the two columns in the array per row index. So when you do setValues it writes in two corresponding columns
Final code:
function testwoD() {
var input = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Raw_data");
var output = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet11");
var row_count = input.getLastRow()
var col_count = input.getLastColumn();
raw_data = input.getRange(1, 1,row_count,col_count).getValues()
tempArr = [] // or new Array
for (var i = 0; i < row_count; i++) {
tempArr[i] = []
tempArr[i][0] = raw_data[i][6].split("?")[0];
tempArr[i][1] = raw_data[i][6].split("?")[1];
}
// Not sure what this code is suppose to achieve? hence removed it
/*var toAddArray = [];
for (i = 0; i < tempArr.length; ++i){
toAddArray.push([tempArr[i]]);
}*/
Logger.log(tempArr)
// You can use setValues and get numof rows and columns using array length
output.getRange(1, 1,tempArr.length,tempArr[0].length).setValues(tempArr);
}
Hope that helps

google apps script two columns summary

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.

optimizing code and how to take selected region of active spreadsheet as input-google docs

I have written script for changing a format like 12.4/12/12.03 into 12:40:00/12:00:00/12:03:00
Here's the code:
function myFunction() {
var sheet=SpreadsheetApp.getActiveSheet();
var rows= sheet.getDataRange();
var numRows=rows.getNumRows();
var values=rows.getValues()
var column = [];
var p = 0;
var k = "H";
for (var i=0;i<numRows;i++) {
// var cell =
//Split the string a the .
var string = values[i][7].split(".");
string[0] = string[0].toString();
p = i+1;
k = "H"+p;
var cell = sheet.getRange(k);
if(string[1]){
string[1] = string[1].toString();
// if the second part is like 4 in 12.4 you set it to 40
if(string[1]!=0) {
if (string[1].length == 1 )
{ string[1] += "0";}
}
// Set the row value to the format you like, here : 12:40:00/12:40
var changed_format = string[0] + ":" + string[1] + ":00";
values[i][7]=changed_format;
p = i+1;
k = "H"+p;
cell.setValue(changed_format);
}
else {
var changed_format = values[i][7]+":00:00";
cell.setValue(changed_format);
}
}
In the above code, I have mentioned columns...i.e., I have to run this script for each column...every time... ex: values[i][7] k="H"+p for 8th column. So, can anyone plz tell me how to do...all at a time...and if possible reduce my code..(optimize)..and also..if is it possible to do like this : if I select the column in the spreadsheet and the changes done by the script applies to that selected region...I mean I want my script to take the selected region as input...is it possible to do..if how.?
One key to optimize your code is to reduce the number of calls to Google services and try getting them done using JavaScript. Here is an optimized version that you could use.
Note that I havent tested it - so if you come across minor syntax errors, feel free to fix them or give a shout if you cannot fix them.
function myFunction() {
var sheet=SpreadsheetApp.getActiveSheet();
var rows= sheet.getDataRange();
var values=rows.getValues();
var COL_H = 8;
var numRows=values.length ; // Length of array = nuymRows. rows.getNumRows();
var column = [];
//var p = 0;
//var k = "H";
var destArray = new Array();
for (var i=0;i<numRows;i++) {
// var cell =
//Split the string a the .
var string = values[i][7].split(".");
string[0] = string[0].toString();
//p = i+1;
//k = "H"+p;
//var cell = sheet.getRange(k);
if(string[1]){
string[1] = string[1].toString();
// if the second part is like 4 in 12.4 you set it to 40
if(string[1]!=0) {
if (string[1].length == 1 )
{ string[1] += "0";}
}
// Set the row value to the format you like, here : 12:40:00/12:40
var changed_format = string[0] + ":" + string[1] + ":00";
//values[i][7]=changed_format;
//p = i+1;
//k = "H"+p;
//cell.setValue(changed_format);
destArray.push([changed_format]);
}
else {
var changed_format = values[i][7]+":00:00";
//values [i][7] = changed_format;
//cell.setValue(changed_format);
destArray.push([changed_format]);
}
}
var destRange = sheet.getRange(1, COL_H, destArray.length, 1);
destRange.setValues(values);
}
TIP: Putting formatted code in the question helps readability