Google Sheets If Function Not running when true - if-statement

I wanted to add a button to a report in google sheets. The button works but you can not use it on a phone so I'm told you must use onEdit. The problem I'm having is to only make changes when the certain box is edited, but when it is the correct box it seems google freezes and doesn't run the script.
I've already tried with my if as (row = 8) , (Row == 8) and (Row == "8").
function onEdit(evt) {
var range = evt.range;
var row = range.getRow().toString();
Logger.log("line 1");
Logger.log("edited!! Row: " + row + " and column: " + range.getColumn ());
if (row == "8"){ // && range.getColumn() = 6 ){ (This bit is for after I figure the row issue out.
Logger.Log("right");
} else {// esstsgs
Logger.log("Wasnt the right cell");
}
Logger.log("Done the thing");
}
So with that code, if I edit any row but 8 my log looks like:
line 1 edited!!
Row x and Column x
done the thing
But if it is row 8 my log says:
line 1
Edited!! Row x and Column x
And nothing more, when is it freezing in the if statement?

Let me know what you would really like to do with this and I'll set it up for you. For now hopefully this will help you to figure the logic.
I don't use Logger.log() for debugging this kind of code. Instead I use the e.source.toast() function.
function onEdit(e) {
var sh=e.range.getSheet();
if(sh.getName()!='Sheet189')return;//this is important so that you don't get edits from unwanted pages
//e.source.toast('Flag1');
if(e.range.rowStart==8 && e.range.columnStart==6) {
var msg=Utilities.formatString('You are editing %s.',sh.getRange(e.range.rowStart,e.range.columnStart).getA1Notation());
SpreadsheetApp.getUi().alert(msg);
}
if(e.range.rowStart!=8 || e.range.columnStart!=6) {
var msg=Utilities.formatString('You are not editing F8. Instead you are editing %s.',sh.getRange(e.range.rowStart,e.range.columnStart).getA1Notation());
SpreadsheetApp.getUi().alert(msg);
}
}
You will probably want to change the sheet number before running this. I didn't set up the Logger.log because I don't like to use it for this kind of code. I did test this and it works fine.

I'd like to point out some mistakes in your code first of all.
You don't need to make the Row variable a string. var row = range.getRow(); is just fine.
You are always Logging "Line 1", why not update it to actually say which line was updated? Logger.log("line " + row);
Your code will output "Done the thing" regardless of what it did. Maybe just say it did anything when it's the right box?
Your fixed code would look like:
function onEdit(evt) {
var range = evt.range;
var row = range.getRow();
var col = range.getColumn();
Logger.log("line " + row + "edited!! Row: " + row + " and column: " + col);
if (row == 8 && col == 6 ) {
Logger.log("Right cell. Did the thing.");
//Call here whatever you want to trigger when this cell changes.
} //Don't need a "else" if you are not going process anything
}
Hope this helps!

Related

Lookup and clear cell content google script

Sorry, I am new to scripting and this is probably a simple one to write; apologies!
I am trying to create a script that gets a range of order numbers in a column, then looks it up on another tab, and then clears a certain cell in that row.
I can get the script to look up the 1st ordernumber at the top of the list andd clear the cell I need clearing, but I cannot work out how to lookup and clear more than one order number at a time.
The logger returns all the values, so I think I need a loop, but I do not knwo where to start with that.
This is for a work project, but I have created a basic sample sheet to play around with:
https://docs.google.com/spreadsheets/d/19koKxFcOfWRz0mEaYs_lHQFgBrU19kDoeYaBY2WBe98/edit?usp=sharing
Can anyone help??
Thanks,
John
Here is the script so far:
function clearpostagecells(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('Order Control');
var sheet2 = ss.getSheetByName('Order Database');
var find = sheet1.getRange(2,2,sheet1.getLastRow()-1).getDisplayValues();
Logger.log(find)
var values = sheet2.getRange(2, 1, sheet2.getLastRow()-1).getDisplayValues();
for (var i = 0; i < values.length; i++) {
var row = "";
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] == find) {
row = i+2;
sheet2.getRange(row,7).clearContent();
}
}
}}

format TextField as you type numbers and grouping

I'm having trouble doing something that should be straightforward simple on a good and clean framework, but with SwiftUI is all a big problem.
I simply need a user to be able to type in a TextField, showing only the numPad, and have the numbers automatically grouped as in 3,000,000 as the user types. Also, if the user taps anywhere in the field to correct a mistaken number, the focus should go to the correct tapped place and the user should be able to erase, keep the focus on the same position, and type the new corrected number. Something as simple as that.
Well, there are multiple problems I'm experiencing:
If using a formatter, the formatted number will only display after the field loses focus (i.e. it will show as 3000000 and then after losing focus, 3,000,000). That shouldn't be a great deal of a problem, but if I focus the field again to add more numbers, it ends up typing something like 3,000,000123 and after losing focus it will not recognize anything after the last 0, so it erases 123. Also, the numPad doesn't have a comma to type it manually.
That's the following case:
#State var number: Int = 0
let numberFormatter = {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.generatesDecimalNumbers = true
numberFormatter.maximumFractionDigits = 0
numberFormatter.groupingSize = 3
return numberFormatter
}()
TextField("1000", value: $number, formatter: numberFormatter)
.keyboardType(.numberPad)
If using Combine, the moment a user tries to correct a digit in the middle of the number, the focus goes to the end of the field, screwing the user experience and making people angry.
For this, I'm using this code:
#State var text: String = ""
TextField("1000", text: $text)
.keyboardType: .numberPad)
.onReceive(Just(text)) { _ in formatNumber() }
func formatNumber() {
if (text.count == 0) {
return
}
let filtered = text.filter { "0123456789".contains($0) }
let groups: Int = filtered.count / 3
let remaining: Int = filtered.count % 3
var res: String = ""
if (remaining > 0) {
res += filtered.substring(with: 0 ..< remaining)
res += ","
}
for i in 0 ..< groups {
res += filtered.substring(with: (remaining + i*3) ..< (remaining + (i+1)*3))
res += ","
}
res.removeLast(1)
text = res
}
Is there any clean solution to something as basic as this?
I use normally the .onChange Modifier directly attached to the TextField, like
TextField("Value:", text: $depotActionWertString)
.onChange(of: depotActionWertString) { _ in
let filtered = depotActionWertString.filter {"-0123456789.".contains($0)}
depotActionWertString = filtered
} // .onChange
It is more compact for the checks of the user input and you may combine it with the .onRecive statement for formatting.
For the first case in simple Versions for displaying text I use often the combination Text with String(format:...., like
Text("\(String(format: "%.2f", self._model.wrappedValue.progress * 100)) %")
I think its more a style question as to be right ore wrong....
I don't know if this is the most optimal solution, but one possible solution using Combine without losing the cursor position is to check the length of the published value.
Since the cursor moves to the end of the text because you updated your textfield with the subscriber, you can solve this problem by preventing your subscriber from updating the published value when you edit your text in the middle. Since when you edit, you always start by deleting, you can then just check in your subscriber whether the length of new value is shorter than the length of your old value. If it is, then just return.
I tried the following implementation and it works:
.sink { value in
let filtered = value.filter { "0123456789".contains($0) }
//check is just an Int saving the length of the old value
if filtered.count > 3 && filtered.count >= self.check {
//withCommas is just a formatter
self.text = String(Int(filtered)!.withCommas())
}
self.check = filtered.count
}
Note there are also some problems with this implementation:
since it will not update when you delete, you may end up with something like this: 123,456,789 and the user deleted "456" to become 123,,789. It won't autocorrect itself. I haven't figured out a way to make the commas autocorrect while keeping the cursor in the same place either.
the above implementation force unwrapped optional, which might cause a crash.

Google Sheets - If statement to populate cell

It feels like this should be really easy, but I keep getting errors related to circular logic.
Column C "Total" will always be entered by the user first. If user enters number in Column B "Variable" then Column A "Fixed" will be populated with C - B. If user enters number in Column A "Fixed", then Column B "Variable" will be populated with C - A.
https://docs.google.com/spreadsheets/d/1xBbU6A_MDK6fyLjdFUD7X06b7BQ1VhQ-FWQBET4cLso/edit?usp=sharing
You are trying to add formulas that will always need to rely on eachother to produce an output and as result of that, it will run into a Circualr Dependency error.
Possible solution:
Try using the "Iterative Calculation" option under File –> Spreadsheet Settings –> Calculation. You can see the description for Iterative Calculation here.
Here is one way to avoid circular references: do not hand enter any formulas, but use an onEdit() script to insert formulas programmatically only when necessary.
The following script will enter a formula in column B when column A is edited, and vice versa:
function onEdit(e) {
if (!e) {
throw new Error('Please do not run the script in the script editor window. It runs automatically when you hand edit the spreadsheet.');
}
const watchSheet = /^(Sheet1|Sheet2|Sheet3|Sheet4)$/i;
const watchColumns = [
{
colNumber: 1,
formula: '=C:C - B:B',
},
{
colNumber: 2,
formula: '=C:C - A:A',
},
];
const sheet = e.range.getSheet();
if (!sheet.getName().match(watchSheet)) {
return;
}
const editedColumn = watchColumns.filter(column => column.colNumber === e.range.columnStart)[0];
if (!editedColumn) {
return;
}
const updateColumns = watchColumns.filter(column => column.colNumber !== editedColumn.colNumber);
updateColumns.forEach(column => {
sheet
.getRange(e.range.rowStart, column.colNumber)
.setFormula(column.formula);
});
}

Best way to run through the entire sheet looking for a text in Google Sheets

I want to create a script that needs to find a certain string and replace it automatically. I've managed to do that, but it takes over 1 minute to run through all rows and columns to find it.
This is what I'm doing now:
for (i=1; i<=rows; i++) {
for (j=1; j<=cols; j++) {
cell = content.getCell(i, j).getValue();
if (content.getCell(i, j).getFormula()) {continue;}
try {
cell = cell.replace (find, replace);
content.getCell(i, j).setValue(cell);
}
catch (err) {continue;}
}
}
The built-in method replaces a text instantly, so I assume there is a better way to approach this. Any ideas?
Instead of retrieving each cell from the sheet one by one, use getDataRange() and getValues() to get all of the data in an array in one call, then perform your search on the array.
Then, depending on if you might have live users editing at the same time your script is run or not, you can either replace the values within the array and re-write the entire sheet with setValues(), or you can use setValue() to update the specific cells with matches one by one as you are currently doing.
See:
https://developers.google.com/apps-script/reference/spreadsheet/sheet#getdatarange
https://developers.google.com/apps-script/reference/spreadsheet/range#getvalues
Try this...
for (i=1; i<=rows; i++) {
for (j=1; j<=cols; j++) {
cellLoc = content.getCell(i, j);
cellValue = cellLoc.getValue();
if (cellLoc.getFormula()) {continue;}
try {
cellValue = cellValue.replace (find, replace);
cellLoc.setValue(cell);
}
catch (err) {continue;}
}
}
I took off every instance of content.getCell(i,j) and instead stored it into cellLoc. That way every time you need content.getCell(i,j), the program doesn't have to find the cell in the content, it can just look what the value of cellLoc is. Let me know if it works and if time has improved...
Well, I don't see much difference between this approach and my first one, but it replaces all matches instantly now:
function findReplace() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var values = range.getValues();
var i, j, find, replace;
find = "hello";
replace = "bravo!!";
for (i = 0; i < values.length; i++) { //Run through all rows
for (j = 0; j < values[i].length; j++) { //Run through all columns
if (values[i][j] == find){
values[i][j] = replace;
}
}
}
range.setValues(values);
}

If the cell contains the word "xxx" then do "xxx"

I am working on a spreadsheet and I am trying to create a script, but I don't know how to do this:
I want to write an if statement where if the last row of column B contains the word, let's say "flower", then do something.
Can you please help me?
Thank you in advance.
Here is a working example of how you can use Google Scripts to do an if statement. Paste this into the script editor, then run it. If A1 has the value 1, then it will change B2 to "Yay!", otherwise B2 will be "Really?!"
function myFunction()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getRange("A1"); //gets a cell to test the value of
if (range.getValue == 1)
{
sheet.getRange("B2").setValue("Yay!") //sets the value of B2 if true
}
else
{
sheet.getRange("B2").setValue("Really?!") //sets the value of B2 if false
}
}
You can also add in else if if you want to add additional conditions, then whatever manipulation or function you want to do needs to inside the brackets