How to remove substring from the cell in google sheets via script - javascript

I am trying to automate the process of parsing data in one of my columns. So far I have it so that after applying a formula I get the data I need but with text around it.
I would like to find and replace the cell values such that I only have the data that is in quotations marks without the quotations. How would I do this via script? Refer to the picture below. This is what I have so far:
var src = ":utm_source=>";
var med = ":utm_medium=>";
var camp = ":utm_campaign=>";
var term = ":utm_term=>";
var con = ":utm_content=>";
var r1 = "";
var data = sheet.getDataRange().getValues();
Logger.log(data.length);
for (var i = 0; i < data[0].length;i++){
for (var j = 0; j < data.length;j++){
Logger.log(data[j][i]);
if(data[j][i].indexOf(src) > -1 || data[j][i].indexOf(med) > -1 || data[j][i].indexOf(camp) > -1 || data[j][i].indexOf(term) > -1 || data[j][i].indexOf(con) > -1){
sheet.getRange(j+1,i+1).setValue(r1);
sheet
}
}
}

Use split('"')[1] (see split()) to retrieve the substring between the quotations.
As a best practice, modify the 2D array you retrieved (data) and use setValues(data), instead of setting every individual with setValue. One way to do that it through a combination of map() and some(), as can be seen below (check inline comments):
var src = ":utm_source=>";
var med = ":utm_medium=>";
var camp = ":utm_campaign=>";
var term = ":utm_term=>";
var con = ":utm_content=>";
var starters = [src,med,camp,term,con]; // Put all starters to check in an array
var range = sheet.getDataRange();
var data = range.getValues();
data = data.map(row => row.map(value => { // Iterate through all rows and cells
var includesStarter = starters.some(starter => value.includes(starter)); // Check if cell value includes a starter
if (includesStarter) return value.split('"')[1]; // Retrieve value within quotations
else return value; // If starter is not included in cell, don't change the array
}));
range.setValues(data); // Write modified data to original range

Related

Rearranging information on spreadsheet using Google Apps Script arrays

Been dealing with this code for a couple of days now. What I want to achieve in the spreadsheet is, move some columns of a single row to another row based on some criteria, and leave the older rows clear of content.
To put it other way, I check at which row columns B, C and H (let's call this row N° '3') match exactly with values at columns B, C and N (let's call this row N° '9'). Then I need to move information from J9:N9 to J3:N3.
So this is the code I've come up with. Problem is, at the end no changes are being made to the spreadsheet nor the 'rows' array when I log it.
function reordenarFacturas(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('AP que esperan');
var lastRow = sheet.getLastRow(); //get the last Row with values
var filteredArray = [];
const rows = sheet.getDataRange().getValues(); // get data as an array
const nombresAP = sheet.getSheetValues(3,2,lastRow-2,2); //get columns 2 and 3 as an array
//Clean nombresAP array from special characters so that later filterLogic works correctly (not using filter anymore, but still wanted to clear names list).
for (h=0; h<lastRow-2; h++){ //rows
for(j=0; j<2;j++){ //columns
nombresAP[h][j] = nombresAP[h][j].toString().toUpperCase();
nombresAP[h][j] = nombresAP[h][j].toString().replace(/á/ig, "A");
nombresAP[h][j] = nombresAP[h][j].toString().replace(/é/ig, "E");
nombresAP[h][j] = nombresAP[h][j].toString().replace(/í/ig, "I");
nombresAP[h][j] = nombresAP[h][j].toString().replace(/ó/ig, "O");
nombresAP[h][j] = nombresAP[h][j].toString().replace(/ú/ig, "U");
nombresAP[h][j] = nombresAP[h][j].toString().replace(" ", " ");
}
}
sheet.getRange("B3:C" + lastRow).setValues(nombresAP);
//End of the cleaning section
//NOT IN USE CURRENTLY, SAVED JUST IN CASE
//Filterlogic works as a way to input several parameters to the filter method used below (now erased).
/*
var filterLogic = function(item){ //function to pass filter data later
if (item[1] === apellidoAP && item[2] === nombreAP && item[7] === periodoFacturado){
return true;
}
else{
return false;
}
}
*/
//Here I check whether certain columns have different values. If they do, then I retrieve some values on variables and push them to an array for later use.
for (var h = 2; h<lastRow; h++){ // h starts at 2 because data starts at row N° 3
if (rows[h][9] == '') continue; // If cells on this columns are empty, I don't need the row and can evaluate the next
else if (rows[h][7] != rows[h][13]){ // If this two different cells of the same row have different values, I then
//assign some data to variables, for later filtering other rows with said data
var apellidoAP = rows[h][1];
var nombreAP = rows[h][2];
var periodoInformado = rows[h][7];
var factura = rows[h][9];
var fechaFactura = rows[h][10];
var nroFactura = rows[h][11];
var montoFactura = rows[h][12];
var periodoFacturado = rows[h][13];
filteredArray.push([[apellidoAP], [nombreAP], [periodoFacturado], [factura], [fechaFactura], [nroFactura], [montoFactura], [periodoFacturado]]);
// Here I'm 'cleaning' some fields in the rows where I've found the differences (doesn't seem to be working)
rows[h][9] == '';
rows[h][10] == '';
rows[h][11] == '';
rows[h][12] == '';
rows[h][13] == '';
}
}
//And finally here, I check whether some filteredArray values match some values in the 'rows' array. If they do match, then I replace some more values in rows array with values from filteredArray. This doesn't seem to be working either.
for (p = 2; p<rows.length; p++){
for (r = 0; r<filteredArray.length; r++){
if (rows[p][1] == filteredArray [r][0] && rows[p][2] == filteredArray[r][1] && rows[p][7] == filteredArray[r][2]){
rows[p][9] == filteredArray[r][3];
rows[p][10] == filteredArray[r][4];
rows[p][11] == filteredArray[r][5];
rows[p][12] == filteredArray[r][6];
rows[p][13] == filteredArray[r][7];
}
}
}
//Finally I set rows back at the spreadsheet with all changes made, but no changes are shown in the spreadsheet once the script ends running.
sheet.getRange("A1:U" + lastRow).setValues(rows);
}
```

How do i shorten time this GAS code(search correct column and data paste)

https://i.stack.imgur.com/7VAJk.png
i want to copy data from "dB" sheet A5:A29 and paste to correct column.
so i use the script to find the correct column.
there range B2:CX2 have 0(not-correct) or 1(correct) value, so i use 'for' & 'if'
BUT!! It's too delay!!
i use console.time() and i get 25909ms(timecheck2 value) !!!
please help me.....
here is my code
function save(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('dB');
console.time("timecheck1");
//find last row
var copyrangeO = sheet.getRange(5,1,25,1).getValues();
var lastrowO = copyrangeO.filter(String).length;
var copyrange = sheet.getRange(5,1,lastrowO,1);
console.timeEnd("timecheck1");
//my dB data start "B2".
var cv = 1;
//find correct value(1). B2 ~ CX2 (#100)
console.time("timecheck2");
for (var i=2; i<101;i++){
if(sheet.getRange(2,i).getValue()===1){
cv = i;
}
}
console.timeEnd("timecheck2");
//if data isn't correct, cv===1. so error msg print.
console.time("timecheck3");
if(cv ===1){
Browser.msgBox("ERROR")
}else {
//data copy and paste.
var columnToCheck = sheet.getRange(4,cv,1000).getValues();
var lastrow = getLastRowSpecial(columnToCheck);
var pasterange = sheet.getRange(lastrow+4,cv);
copyrange.copyTo(pasterange, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
Browser.msgBox(lastrowO + " saved!");
}
console.timeEnd("timecheck3");
}
Issue:
If I understand your situation correctly, you want to find the cell in B2:CX2 in which the value is 1, but the script is taking too much time for this.
The problem here is that you are using getRange and getValue in a loop (sheet.getRange(2,i).getValue()===1). This greatly increases the amount of calls to the Sheets service, which slows down your script, as you can see at Minimize calls to other services.
Solution:
In that case, I'd suggest doing the following:
Get the values from all columns at once using getValues().
Use findIndex to get the column index for which value is 1.
In order to do that, replace this:
var cv = 1;
//find correct value(1). B2 ~ CX2 (#100)
console.time("timecheck2");
for (var i=2; i<101;i++){
if(sheet.getRange(2,i).getValue()===1){
cv = i;
}
}
With this:
var ROW_INDEX = 2;
var FIRST_COLUMN = 2; // Column B
var LAST_COLUMN = 102; // Column CX
var columnValues = sheet.getRange(ROW_INDEX, FIRST_COLUMN, 1, LAST_COLUMN-FIRST_COLUMN+1).getValues()[0];
var cv = columnValues.findIndex(columnValue => columnValue === 1) + FIRST_COLUMN;
Note:
If there's no cell in the range with value 1, findIndex returns -1 which, added to FIRST_COLUMN, results in 1. That's appropriate for your current script, but won't work if the FIRST_COLUMN stops being 2, so be careful with this (either change the condition if(cv ===1){ to something less strict, or don't assign the resulting value to cv if findIndex returns -1).
The function will spend most of its time in the for loop because it repeats the Range.getValue() call many times. You can speed things up quite a bit by getting all values with one Range.getValues() call, like this:
let cv = 1;
console.time("timecheck2");
sheet.getRange('B2:B100').getValues().flat()
.some((value, index) => (cv = 2 + index) && value === 1);
console.timeEnd("timecheck2");
Note that this is not a cleanest way of finding cv, but it should help illustrate why you have a performance issue. You may want to do a complete rewrite of the code, using declarative instead of imperative style.
Try this:
I don't know what you're doing in the save because to did not supply the helper function code.
function save(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('dB');
var vs0 = sh.getRange(5,1,25,1).getValues();
var lr0 = vs0.filter(String).length;
var crg = sh.getRange(5,1,lr0,1);
var cv = 1;
const vs1 = sh.getRange(2,2,1,99).getValues().forEach((c,i) => {
if(c == 1)cv = i + 2
})
if(cv == 1){
Browser.msgBox("ERROR")
}else {
var vs2 = sh.getRange(4,cv,1000).getValues();
var lastrow = getLastRowSpecial(vs2);
var drg = sh.getRange(lastrow+4,cv);
crg.copyTo(drg, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
Browser.msgBox(lr0 + " saved!");
}
}

Locating all cell's positions in google sheets

The problem is: I have big spreadsheet (more than 4500 rows) with a lot of data in the first column - for ex. with types of fruits, which are not unique, like this:
APPLE
BANANA
APRICOTS
APPLE
BLACKCURRANT
APPLE
BANANA
APRICOTS
etc.
What I need - locate each BANANA, to be able to put in cell beside some info, for ex. YES. I tried to loop solution from Locating a cell's position in google sheets using a string or an integer but for sure my code is wrong. I already spent a lot of hours to invent something, but still don't understand what I'm missing.
function test(){
var dispatch = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("FRUITS");
var find = dispatch.getRange("A:A").getValues();
var name = "BANANA";
var lastRow = dispatch.getLastRow();
var n = 1;
var temp = dispatch.getRange(n, 2).getValue();
var i = 0;
while (temp != ""){
for(var n in find){
if(find[n][0] === name){break}
}
n++;
var n = n + i;
dispatch.getRange(n, 2).setValue("YES");
var temp = dispatch.getRange(n, 2).getValues();
var find = dispatch.getRange(n, 2, lastRow).getValues();
var i = n;
}
}
I will be very grateful for the help.
The code example is below:
function test(){
var dispatch = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("FRUITS");
var range = dispatch.getRange(1, 1, dispatch.getLastRow(), 2);
var values = range.getValues();
values.map(function(row) {
if (row[0] == "BANANA")
row[1] = "YES";
});
range.setValues(values);
}
JS array map() method does the most part of work. We convert range values to JS array and back after mapping completes.

Ignore empty rows from a loop in Google Apps Script

I'm new to Google Apps Script and I'm trying to ignore the empty rows from a for loop, but I'm still getting the empty rows in my log. Here are my codes,
function getNonEmptyRows() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Bldng4");
var lr = sheet1.getLastRow() - 17;
for (var i = 1; i < lr; i++) {
var singleRow = sheet1.getRange(i, 1, 1, sheet1.getLastColumn()).getValues();
if (singleRow.length > 0) {
Logger.log(singleRow);
}
}
}
How can I get the only non empty rows from the loop? Need this help badly. Thanks.
var range_data =
sheet1.getRange("A2:A") //Column Range
.getValues() //Get array from range values
.filter(array=>array != '') //Filter non-empty values
I was looking for a solution to a similar problem and here is what I did:
first, I found this tutorial on how to create google apps script to eliminate duplicate rows.
next, i modified it like this to eliminate empty rows:
function removeEmptyRows() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("settings");
var data = sheet.getRange("A2:D").getValues();
var newData = new Array();
for(i in data){
var row = data[i];
var empty = false;
for(i in data){
if(row.toString() == ",,,"){
empty = true;
}
}
if(!empty){
newData.push(row);
}
sheet.getRange(2, 6, newData.length, newData[0].length).setValues(newData);
};
As you can see, it takes A2:D range, removes empty rows and then pastes filtered data without empty rows into range F2:I.
You can try to use this script, but you may need to adjust it to "width" of your array. To do so change the number of commas in the following string:
if(row.toString() == ",,,"){
edit:
I modified script a bit to automatically adjust to width of your array:
function removeEmptyRows() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("settings");
var range = sheet.getRange("A2:C");
var data = range.getValues();
var dataWidth = range.getWidth();
var newData = new Array();
if (dataWidth<=1) {
var stringToCompare = '';
}else{
var stringToCompare = ',';
for (var i=0;i<dataWidth-2;i++) stringToCompare+=","
};
for(i in data){
var row = data[i];
var empty = false;
for(i in data){
if(row.toString() == stringToCompare){
empty = true;
}
}
if(!empty){
newData.push(row);
}
}
sheet.getRange(2, 6, newData.length, newData[0].length).setValues(newData);
};
The getValues() method you are using always return a 2 dimensions array, whatever the contents of the cells might be. There are several ways to get the cells content, one of them is to stringify the range content (convert matrix to single string) , remove the commas (and eventually "invisible" spaces) and check the length of the resulting string.
replacement code could go like this :
var singleRow = sheet1.getRange(i, 1, 1, sheet1.getLastColumn()).getValues().toString().replace(',','').replace(' ','');
That said, this code is very inefficient because it uses SpreadSheetApp service in each loop iteration which is particularly slow.
You'll find better approches in the documentation about best practices.

Pull data from one Google spreadsheet to another using Google script

I have 2 spreadsheet in my Drive. I want to pull data from a cell in 1 spreadsheet and copy it in another.
The spreadsheet "TestUsage" will sometimes have data in column A, but none is column B.
I would like so that when I open the spreadsheet, it would populate that empty cell in sheet "TestUsage" from sheet "TestParts".
Here is my code:
var ssTestUsage = SpreadsheetApp.getActiveSpreadsheet();
var sTestUsage = ssTestUsage.getActiveSheet();
var lastRowTestUsage = sTestUsage.getLastRow();
var rangeTestUsage = sTestUsage.getSheetValues(1, 1, lastRowTestUsage, 4);
var TESTPARTS_ID = "1NjaFo0Y_MR2uvwit1WuNeRfc7JCOyukaKZhuraWNmKo";
var ssTestParts = SpreadsheetApp.openById(TESTPARTS_ID);
var sTestParts = ssTestParts.getSheets()[0];
var lastRowTestParts = sTestParts.getLastRow();
var rangeTestParts = sTestParts.getSheetValues(1, 1, lastRowTestParts, 3);
function onOpen() {
for (i = 2; i < lastRowTestUsage; i++) {
if (rangeTestUsage[i][0] !== "" && rangeTestUsage[i][1] == "") {
for (j = 1; j < lastRowTestParts; j++) {
if (rangeTestUsage[i][0] == rangeTestParts[j][0]) {
Logger.log(rangeTestUsage[i][1]);
Logger.log(rangeTestParts[j][1]);
rangeTestUsage[i][1] = rangeTestParts[j][1];
break;
}
}
}
}
}
The problem with this is this doesn't do anything:
rangeTestUsage[i][1] = rangeTestParts[j][1];
I know there must be a method that can get data from one range to another.
Please let me know if I am totally incorrect or I am on the right path.
the statement
"this doesn't do anything:"
rangeTestUsage[i][1] = rangeTestParts[j][2];
is not really true... and not really false neither..., actually it does assign the value to rangeTestUsagei but you dont see it because it is not reflected in the spreadsheet.
Both values are taken from the Sheet using getValues so at that time they are both array elements.
What is missing is just writing back the array to the sheet using the symetrical statement setValues()
Give it a try and don't hesitate to come back if something goes wrong.
EDIT :
I didn't notice at first that you were using getSheetValues instead of getValues (simply because I never use this one)... the only difference is that getValues is a method of the range class while yours belongs to the sheet class; the syntax is similar in a way, just use
Sheet.getRange(row,col,width,height).getValues()
it takes one word more but has the advantage to have a direct equivalent to set values
Sheet.getRange(row,col,width,height).setValues()
Serge insas has a good explanation of why your code doesn't work and hints at the solution below.
I recommend you use an array to store the updated values of column B that you want then set the entire column at the end.
Modifying your code...
var ssTestUsage = SpreadsheetApp.getActiveSpreadsheet();
var sTestUsage = ssTestUsage.getActiveSheet();
var lastRowTestUsage = sTestUsage.getLastRow();
var rangeTestUsage = sTestUsage.getSheetValues(1, 1, lastRowTestUsage, 2);
var TESTPARTS_ID = "1NjaFo0Y_MR2uvwit1WuNeRfc7JCOyukaKZhuraWNmKo";
var ssTestParts = SpreadsheetApp.openById(TESTPARTS_ID);
var sTestParts = ssTestParts.getSheets()[0];
var lastRowTestParts = sTestParts.getLastRow();
var rangeTestParts = sTestParts.getSheetValues(1, 1, lastRowTestParts, 2);
var colB = [];
function onOpen() {
for (i = 2; i < lastRowTestUsage; i++) {
if (rangeTestUsage[i][0] !== "" && rangeTestUsage[i][1] == "") {
var matched = false;
for (j = 1; j < lastRowTestParts; j++) {
if (rangeTestUsage[i][0] == rangeTestParts[j][0]) {
//Logger.log(rangeTestUsage[i][1]);
//Logger.log(rangeTestParts[j][1]);
colB.push([rangeTestParts[j][1]]); // push the value we want into colB array
matched = true;
break;
}
}
if(!matched) // this is in case you don't have a match
colB.push([""]); // incase we don't have a matching part
} else {
colB.push([rangeTestUsage[i][0]]); // we already have a value we want so just add that to colB array
}
}
sTestUsage.getRange(2,2,lastRowTestUsage).setValues(colB); // update entire column b with values in colB array
}

Categories