Altering find and replace Google Sheets script - javascript

I have the following script, though I would like to change it to only find and replace on a specific sheet (Sheet4!A:AB).
How would I go about doing this:
function fandr() {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var s=ss.getActiveSheet();
var r=s.getDataRange();
var vlst=r.getValues();
var i,j,a,find,repl;
find="abc";
repl="xyz";
for (i in vlst) {
for (j in vlst[i]) {
a=vlst[i][j];
if (a==find) vlst[i][j]=repl;
}
}
r.setValues(vlst);
}

I think that pnuts's comment is helpful for your situation. If you are still looking for your solution, how about this answer? You want to find and replace the values in Sheet4!A:AB. If my understanding is correct, I would like to propose 2 patterns. I think that there are several solution for your situation. So please think of this as one of them.
Pattern 1
This modification is a simple modification. It modified the range for retrieving values to Sheet4!A:AB.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var r = ss.getRange("Sheet4!A:AB");
var vlst = r.getValues();
var i, j, a, find, repl;
find = "abc";
repl = "xyz";
for (var i = 0; i < vlst.length; i++) {
for (var j = 0; j < vlst[i].length; j++) {
a = vlst[i][j];
if (a == find) vlst[i][j] = repl;
}
}
r.setValues(vlst);
Pattern 2
In the case of pattern 1, the values retrieved by getRange("Sheet4!A:AB") includes the empty rows. By this, the search speed becomes slow. So in this pattern 2, the data range of "A1:AB" is retrieved using getLastRow().
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("Sheet4");
var r = s.getRange("A1:AB" + s.getLastRow());
var vlst = r.getValues();
var i, j, a, find, repl;
find = "abc";
repl = "xyz";
for (var i = 0; i < vlst.length; i++) {
for (var j = 0; j < vlst[i].length; j++) {
a = vlst[i][j];
if (a == find) vlst[i][j] = repl;
}
}
r.setValues(vlst);
References :
getSheetByName()
getRange(a1Notation)
getLastRow()
If I misunderstand what you want, please tell me. I would like to modify it.

Related

Finding two elements within an array with IF statement using Apps Script

I am very new to programming and I am trying to locate two consecutive values within a 1-d array in google sheets and the answers need to be highlighted in bold in the google sheet. The first value should be greater than 5 (located for example in cell D10) and the next value should be less than 4.99 (and located in E10). I have tried two methods but can't seem to get the Code to work - I don't know if I am over-complicating the problem or not. The first method was to make the row into 2 separate arrays of elements so that if element J is greater than 5, and element k (which looks at the element next to J) is less than 4.99.
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var ui2 = SpreadsheetApp.getUi();
var data1 = ss.getRange(10,2,1,20).getValues()[0]; //Variable 1 - to find the ON value B10:U10
var data2 = ss.getRange(10,3,1,20).getValues()[0]; //to find the OFF value;
var onA = 5.00;
var on = data1 > onA;
var offA = 4.99;
var off = data2 < offA;
for(var i = 0; i < 20; i++){
for(var j = 0; j < 20; j++){
if(data1[i] < 5.00 && data2[j] < 4.99){
ss.getRange(10,2,1,20).setValue(data1[i]).setFontSize(12).setFontWeight("bold");
ss.getRange(10,3,1,20).setValue(data2[j]).setFontSize(12).setFontWeight("bold");
break;
}
else {
ui2.alert("the values are not found");
break;
}}}}
My other method was not search the cells as a single array - this works but once I add a message to a else statement to state the values are not found it would repeat this alert for the same number of times as the loop.
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var ui2 = SpreadsheetApp.getUi();
for(var j = 2; j < 22; j++){ // j is column number
for(var k = j+1; k < 23; k++){
var result = ss.getRange(10,j).getValue();
var result2 = ss.getRange(10,k).getValue();
var resulta = result > 5.00;
var resultb = result2 < 4.99;
if (resulta == true && resultb == true) {
ss.getRange(10,j).setValue(result).setFontSize(12).setFontWeight("bold");
ss.getRange(10,k).setValue(result2).setFontSize(12).setFontWeight("bold");
break;
} else {
break;
ui2.alert('Values not found');
}}}}
Any help would be amazing and very greatly appreciated!
Try this:
function findconsecutives() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1')
const rg=sh.getRange(10,2,1,20);
const vs=rg.getValues()[0];
for(let i=0;i<vs.length-1;i++) {
if(vs[i]<5 && vs[i+1]<4.99) {
sh.getRange(10,i+2,1,2).setFontSize(12).setFontWeight('bold');
break;
}
}
ss.toast('not found');
}
I just changed the fontsize and fontweight to the cells that meet the requirements if you want to change feel free. I don't want to.

getting code to run on multiple sheets in a spreadsheet

I have 4 sheets in my GoogleSheets spreadsheet.
I need the code below to run on every sheet EXCEPT the one called "MRS summary".
I can't seem to get that 'for' loop (the one with the 'g' variable) to work though, because the code only runs for the ACTIVE sheet. But I do know that the Logger.log(g) line is picking up the right indexes...so I think the 'for g' loop is working in the sense that it knows what sheets to look at, but the rest of the code only does what it's supposed to do on the ACTIVE sheet....but I need it to run on every sheet except the one called "MRS summary". Any thoughts?
function SearchCols()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
skip = ['MRS summary'];
for (var g = 0, len3 = sheets.length; g < len3; g++)
{
var name = sheets[g].getName()
if (skip.indexOf(name) > -1) continue;
{
var expen = ss.getSheetByName("MRS summary")
var sheet1 = expen.getRange("A13:A210").getValues();
var searchCol1 = expen.getRange ('B13:B210').getValues();
var searchCol2 = expen.getRange ('A13:A210').getValues();
var searchCol3 = expen.getRange('C13:C210').getValues();
var searchCol4 = expen.getRange('D13:D210').getValues();
var searchCol = ss.getRange('A13:A210').getValues();
var searchVal = ss.getRange('B4').getValue();
for (var i = 0, len = searchCol2.length; i < len; i++)
for (var j = 0, len2 = searchCol1.length; j < len2; j++)
{
if (searchCol2[j][0] == searchVal && searchCol1[j][0] ==
searchCol[i][0])
{
col1[g].getRange(i + 13, 4).setValue(searchCol3[j][0])
col1[g].getRange(i + 13, 5).setValue(searchCol4[j][0])
}
}
}
Logger.log(g);
}
}

get line number of xml node in js

I'm trying to validate (custom rules) a xml source. Therefore I parse the source with document.evaluate and a certain xpath and validate the result nodes.
If a node is not correct, I would like to give an error with the nodes line number in the source.
How can I go about accomplishing this?
I had similar problem and I wrote a function that finds the n-th tag on the original string based on the result of getElementsByTagName.
It is something like this:
function xmlNodeToOriginalLineNumber(element, xmlRootNode, sContent) {
var sTagName = element.tagName;
var aNodeListByTag = xmlRootNode.getElementsByTagName(sTagName);
var iMaxIndex = 0;
for (var j = 0; j < aNodeListByTag.length; j++) {
if (aNodeListByTag.item(j) === element) {
iMaxIndex = j;
break;
}
}
var regex = new RegExp("<" + sTagName + "\\W", 'g');
var offset = 0;
for (var i = 0; i <= iMaxIndex; i++) {
offset = regex.exec(sContent).index;
}
var line = 0;
for (var i = 0; i < sContent.substring(0, offset).length; i++) {
if (sContent[i] === '\n') {
line++;
}
}
return line + 1;
}
I updated your sample: https://jsfiddle.net/g113c350/3/

Create an Array with Data from TD Tags

I want to export an HTML table into a spreadsheet that will be created by the JavaScript program. I've considered doing this via the JQuery library but I don't know much about it nor do I think that it will be allowed in my situation. I have seen a solution that uses XML but I don't know much about XML either. What I intend on doing is creating a 2 dimensional array and exporting the contents of that array into Excel. Unfortunately, I don't have control over the tagging scheme and I will have to use getElementsByTagName. The tagging scheme is created via a reporting software.
The below code loops to the second row but it stops at the second row. I can't figure out what I am doing wrong. I hope that somebody will be able to explain a remedy to the error.
<!DOCTYPE html>
<html>
<body>
<table>
<tr><td>a</td><td>1</td></tr>
<tr><td>b</td><td>2</td></tr>
<tr><td>c</td><td>3</td></tr>
<tr><td>d</td><td>4</td></tr>
</table>
<script>
var myNodelist = document.getElementsByTagName("td");
var NumTD= (document.getElementsByTagName("td").length);
var NumTR= (document.getElementsByTagName("tr").length);
var NumCol=NumTD/NumTR;
var array1 = [[]];
var c=0;
var r=0;
alert("NumTD is: "+NumTD);
for (var i=0;i<=NumTD;i++) {
alert("On TD: "+i);
alert(myNodelist[i].innerHTML);
alert("r: "+r);
alert("c: "+c);
array1[r][c]=myNodelist[i].innerHTML;
alert("The array is at: "+array1[r][c]);
if((c+1)==NumCol) {
c=0;
} else {
c++;
}
if(c==0) {
r++;
}
}
</script>
</body>
</html>
Issue 1: Work on your formatting. It's a lot easier to debug your own code when you indent correctly, use new lines effectively, and use descriptive variable names.
Issue 2: Uncaught TypeError: Cannot set property '0' of undefined on line 19. You're trying to set array1[0][0] when array1[0] isn't an array. You need to make array[0] an array before you try to use it as one, like this:
if(!array1[i]){
array1[i] = [];
}
This checks if your array exists. If it doesn't, then it is set to an empty array.
Issue 3: Uncaught TypeError: Cannot read property 'innerHTML' of undefined on line 24. Your loop runs from 0 to 8 and myNodeList only goes up to 7. Changing for (var i=0;i<=NumTD;i++) to for (var i=0;i<NumTD;i++) solves this issue by restricting the bounds of the loop.
Issue 4: Messing around with 3 indices (r, c, and i) is unnecessary and doesn't make things any easier. For one, c and i are always equal, so we can run the loop from c = 0 to c < numTD.
Issue 5: Checking if c + 1 is equal to the number of columns is clumsy. Instead, your second dimension should be c % numCol, which is just the remainder of c divided by numCol. It works perfectly here. We can also increment r when c % numCol is 0.
var myNodelist = document.getElementsByTagName("td");
var numTD = myNodelist.length;
var numTR = document.getElementsByTagName("tr").length;
var numCol = numTD/numTR;
var array1 = [];
var r = 0;
for(var c = 0; c < numTD; c++){
array1[r] = array1[r] || [];
array1[r][c % numCol] = myNodelist[c].innerHTML;
if(c % numCol){
r++;
}
}
Working example
Issue 6: This approach is generally a clumsy way to accomplish the task. It'd be a lot cleaner to grab each row, iterate through them, and grab the individual cells then. Edit: Got this is right before I saw #dc5's edit. His works just as well.
var rows = document.getElementsByTagName("tr");
var results = [];
for(var i = 0; i < rows.length; i++){
var cells = rows[i].getElementsByTagName("td");
results[i] = [];
for(var j = 0; j < cells.length; j++){
results[i][j] = cells[j].innerHTML;
}
}
Working example
With your current logic, you have a couple of problems:
array1 is initialized as [[]]
r (the row counter) is never incremented
Your for loop: for (var i=0;i<=NumTD;i++) should be for (var i=0;i<NumTD;i++)
The values read from the table are always updating the value at array1[0,0] and array1[0,1]
The fix - for the current logic, is to increment r when ever the column counter is reset to zero and to insert a new empty array at the new row if it doesn't already exist.
Code:
var myNodelist = document.getElementsByTagName("td");
var NumTD = (document.getElementsByTagName("td").length);
var NumTR = (document.getElementsByTagName("tr").length);
var NumCol = NumTD / NumTR;
var array1 = []; // <== initialize as empty array
var c = 0;
var r = 0;
for (var i = 0; i < NumTD; i++) {
array1[r] = array1[r] || []; // <== If array at row doesn't exist,
// initialize it to a new array
array1[r][c] = myNodelist[i].innerHTML;
if ((c + 1) == NumCol) {
c = 0;
r++; // <== increment the row
} else {
c++;
}
}
jsFiddle demo
The above code could be simplified a bit by querying the table rows first, then iterating the rows, querying the child td's for each found row.
For example:
var rows = document.getElementsByTagName("tr");
var rowCount = rows.length;
var r, c, cols, colCount;
var array1 = [];
for (r = 0; r < rowCount; ++r) {
array1[r] = [];
cols = rows[r].getElementsByTagName("td");
colCount = cols.length;
for(c = 0; c < colCount; ++c) {
array1[r][c] = cols[c].innerHTML;
}
}
jsFiddle demo

Passing a calculated variable from one function to another

...not sure "calculated" was the right word...I have written two functions, the second of which needs the output of a variable from the first. I cant seem to get it to pass...my guess is that I am calling it wrong, but can't seem to get it right...might have something to do with the time I've spent staring at the whole thing..
The variable I need passed is subset I am trying to use it on the last line of the second function.
If it matters, the getPos function is getting its value from an input box.
The javascript:
<script>
var alphabet = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"];
function getPos(value)
{
var letterPosition = alphabet.indexOf(value);
var subset = alphabet.slice(letterPosition+1, 26);
document.getElementById('theRest').value = subset;
}
function appendTable(id)
{
var tbody = document.getElementById(id).getElementsByTagName("tbody")[0];
var i = 0;
for (var r = 0; r < 4; r++) {
var row = tbody.insertRow(r);
for (var c = 0; c < 4; c++) {
var cell = row.insertCell(c);
cell.appendChild(document.createTextNode(subset[i++]));
}
}
}
</script>
<script>
var alphabet = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"];
var subset;
function getPos(value)
{
var letterPosition = alphabet.indexOf(value);
subset = alphabet.slice(letterPosition+1, 26);
document.getElementById('theRest').value = subset;
}
function appendTable(id)
{
var tbody = document.getElementById(id).getElementsByTagName("tbody")[0];
var i = 0;
for (var r = 0; r < 4; r++) {
var row = tbody.insertRow(r);
for (var c = 0; c < 4; c++) {
var cell = row.insertCell(c);
cell.appendChild(document.createTextNode(subset[i++]));
}
}
}
</script>
That should do the trick.
Declaring subset before the functions makes it a global var, if you define it with var subset within a function it becomes tied to that function, removing the var makes it use the global var.

Categories