Query to count cells changes cell's range randomly, by itself - google-apps-script

I refer to my previous question.
The problem happens when I use the non-array solution proposed by #Martín (the one that overloaded less my system).
=QUERY(Sheet1!A2:B;"Select A,COUNT(A) where A is not null group by A pivot B")
By itself the Query() works perfectly, but only with non-dynamic ranges. The function, put in "Sheet2", draws the data to be counted from "Sheet1". The range in which the function is applied is a portion of "Sheet1", that's why I entered "Sheet1!A2:B".
In "Sheet1", new cells are constantly added and the sheet automatically organizes the answers into different categories (first_department, second_department, ...). To do this, it adds blank rows between groups of cells (different categories), removes rows in the wrong category to move them in the correct one, removes all blank rows at the end (useless part of the sheet).
At this point, the Query() in "Sheet2" has some error which leads it to consider a range outside the one originally set (it's always the number of cells on the sheet + 1), raising a "#VALUE!".
Function before:
=QUERY(Sheet1!A2:B;"Select A,COUNT(A) where A is not null group by A pivot B")
Function after (an example):
=QUERY(Sheet1!A23:B;"Select A,COUNT(A) where A is not null group by A pivot B")
In this case there were 22 cells in "Sheet1", and the Query() went to "A:23".
I've tried myself to solve the issue in the past week, trying to simulate the situation that led to the problem. I've updated the organization of "Sheet1" by adding new data or deleting some: I haven't been able to find out the source of the error. When I checked the query's behaviour, the range rightly locked at "A2:B", it didn't end up at "A23:B".
I tried deleting the entire sheet and creating a new one from scratch (maybe it was a local bug), I tried changing Google Sheet's country of use (maybe it was punctuation), but that didn't fix anything. I've looked at the Query() and it's, indeed for me, correct under all shapes.
Is there a way to lock the range, so that it doesn't change randomly?
I'd appreciate any effort!

Yes, it's a common issue, you can use INDIRECT to lock the range:
=QUERY(INDIRECT("Sheet1!A2:B");"Select A,COUNT(A) where A is not null group by A pivot B")

Related

Sorting Columns Based on Values from different sheet

I've created a table with Names, Type of Sale, Location and Amount. The names is taken from a different sheet with formula ={"";Unique(rawData!A:A)} and for other columns like location Amount and type of sales, I've used Vlookup and Sumifs function based on the requirement.
This is working fine, but, when I try to sort with the googlescript with the code
function autoSort() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const ws = ss.getSheetByName("Summary")
const range = ws.getRange("A2:D1000")
range.sort({column:4, ascending: false})
}
the Amount column is getting sorted but again it is changing back in a blink.
Is this due to the formula ={"";Unique(rawData!A:A)} or I'm doing something else wrong here.. Kindly help
What's going wrong?
When you are "manually" sorting your data with the script, your ={"";Unique(rawData!A:A)} formula does not change. All you are doing is rearranging the other vlookup and sumif physical formulas around the sheet. Since the names never change, neither are your resulting values.
This is a very basic and rudimentary explanation. I'm sure there is a much better explanation for what is happening, but my basic understanding is just that you can't sort formulas because ultimately nothing about the order actually changes
A solution?
Do do everything in one formula in order to group everything together.
I have come up with one to do this.
=ifna(
sort(
{
Unique(rawData!A2:A),
arrayformula(VLOOKUP(Unique(rawData!A2:A), rawData!A2:D10, 2, 0)),
arrayformula(VLOOKUP(Unique(rawData!A2:A), rawData!A2:D10, 3, 0)),
arrayformula(VLOOKUP(Unique(rawData!A2:A), rawData!A2:D10, 4, 0))
},
4, 0
)
)
This pulls all the data at once (including the names) and sorts it as a whole. The VLOOKUP references the unique array, instead of an actual cell reference. This allows the values to be sorted, without rearranging the physical formulas. Below is a link to the spreadsheet I created to test this. This may need to be changed and edited to your liking (especially given that I didn't incorporate any SUMIF formulas), but it does what you appear to be asking for.
https://docs.google.com/spreadsheets/d/1Yj0IhZsbbXvhHQfkhn3P8Ac-A8nq5D3fiOZAsVYiPVs/edit?usp=sharing
This formula creates a blank first row. This can be fixed by filtering out blank values in the unique formula, but I left it as it was right now to avoid confusion. If you would like help incorporating that, just let me know and I can edit the formula.

How to find out the order of sheets created?

This is important because the order of execution is based on the first created sheet to the last created sheet which ordinarily is not an issue but is an issue for circular references. Obviously I can put something in the name to remind me but there seems to be no pattern to the gid number and therefore have no way to know for already created spreadsheets with custom sheet names without painfully testing circular reference formulas.
This is not part of my question but here is an example to demonstrate why this is so important:
Step 1) File->Spreadsheet Settings->Calculation->Iterative Calculation(On)->Max number of iterations(1)
Step 2) Set up the following test formulas
Cell A1 =A1+1
Cell B1 =A1
Cell A2 =B2
Cell B2 = B2+1
You will notice that even though they formulas should have the same results they do not due to the execution order, now this example is just of a single sheet but invisibly the original sheet creation order is the order of execution when formulas reference other sheets.
The fastest solution that satisfies your request is to use the getSheets method to obtain an array where every sheet appears in the same order that Sheet shows in the user interface. Keep an eye on them, because if you move one sheet around in the user interface the array won't reflect the true order of creation.
Another approach, more precise than the former one, is to use the properties class to save a timestamp every time that a sheet is created. You can manage that with the setProperty and getProperties methods.

Copying values from Google sheet, add time stamp, prevent duplicates

I'm using Kimono to scrape a site that lists active development permits. For a one off data scrape it's fine, the problem is that there is no way of sorting new data. Every time Kimono scrapes it updates the entire array.
This is what the sheet currently looks like
https://docs.google.com/spreadsheets/d/1BH8ESAHQJrog6x8nRBOpgBN-nTN1_aDY7wr8W_YYet0/edit#gid=1865015934
The first sheet is automatically populated and overwritten by Kimono. It seems like the most logical way of making this work would be to copy the values to another sheet, adding a time stamp when this happens and then preventing duplicate values from being posted.
Following this thread is was able to muster this code
I've got the copying part down with the following:
function moveValuesOnly() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getRange('Building Permits!A1:D');
source.copyTo(ss.getRange('Sheet2!A1'), {contentsOnly: true});
source.clear();
}
What I am trying to figure out is how to prevent duplicates based on the URL value.
I know that it is right in front of me, but I'm drawing a blank on how to get this to work.
This Google documentation article on removing duplicates is very well written, so I won't duplicate it: https://developers.google.com/apps-script/articles/removing_duplicates
It has exactly what you need. Read the later part of the article where it talks about how to check duplicates not for the entire row, but specific columns in that row. Once you understand that, your problem is straightforward to solve. Just use 2 arrays, to hold the contents of the rows from the 2 sheets as in the example they've given. compare the first column value of the current row. if it matches, don't copy the row over.
note: this works only when you copy row-by-row into the target sheet, not the entire range as your'e doing right now. But that's unavoidable.

Keeping value after reference cell has been changed

I need cell B3 to reference B1 while blank.
Once you put something in B1 it'll keep that value forever, even once B1 get's changed to something else.
This is my situation:
Basically I have a sheet that is fed by a Google form and each submission needs three key reference numbers each kept in columns a,b,c
A = Unit Number/Individuals name (There may be duplicates down the sheet as this is per submission)
B = Work Order (Imputed by me after actual work on unit has been done)
C = Cry Number/Reference number (Automatically generated per submission; no duplicates)
I then have a frozen row at the top which contains a search bar that you can search for the cry number (A1)(Which has a Data Validation set to column C so that you can only search valid cry numbers) and then a cell to add a W/O to that Cry Number (B1)
In column B3:1000, I have this formula copied down:
B3=if(isblank($C3),"",if($A$1=$C3, SUBSTITUTE($B$1,"",$B$1),""))
...which makes it so that if you select say "CN-168" (A valid cry number) and in 'B1' type "W1134" that work order number will be assigned.
Now I need that work order to stay there regardless of when 'A1' changes so that you can do the process over again on another submission.
Is it possible to do with formulas? If not, then a Google Script?
Here is a template of what I'm dealing with but not to the same scale as my Data Base
Its not possible with formulas but easily done with apps script. look at the onEdit trigger and the documentation for SpreadsheetApp to setValues to the appropiate ranges.
If you want to be 100% complete you also need a time trigger (say every 10 minutes) to check that a row wasnt missed. It can be missed during apps script errors/outages or when the sheet is changed from outside the sheets webpage/app (For example using the http spreadsheet api)

Workaround for not losing cell's reference because of user doing sorting, while script checks cells value

I have been struggling with the following situation for almost the whole last week and it would be awesome if someone could give me some hint.
The situation:
1. Script finds a particular value in, lets say, 'Sheet1', and gets the row number of the cell containing this value.
2. Since script has found this value, it executes a bunch of actions like creating new spreadsheet and copying numerous 'Sheet1' from dozen other spreadsheets to this newly created spreadsheet, and comparing/ analyzing data.
The problem.
- While script is doing a bunch of other actions, user is able to rearrange cells by, for example, sorting A to Z, which changes the address of previously found cell.
Here is the code that I used in order to verify this:
function WhatHappensIfUserSorts () {
var ss = SpreadsheetApp.getActive();
var sh = ss.getActiveSheet();
var rng = sh.getRange("B17"); //Lets say that script finds this cell according to some rules
Utilities.sleep(10000);
rng.setValue("Test Value");
}
Question:
Might there be any workaround for this?
My current ideas.
1. I was thinking about hiding the filter row in the beginning of the script, but this doesn't help a lot, because users can insert new row in the which will change the addresses of the rows below.
The background.
I am trying to create two way synchronization, meaning, each project member has his/ her own spreadsheet with 'Project X', 'Project Y' etc. sheets and no matter who updates their project sheets, all other users that work on the same project get these updates in their project sheets. These updates that have to be tracked are not just the cell values, these are cell notes as well. And this is the reason why script has to do the bunch of other actions, since CopyTo method does not work between spreadsheets.
During my research I found sheetSpider project, but it seems somewhat different and too complicated from what I need.
A simple suggestion would be to give each row a unique identifier so that you could use it to evaluate the target range again before you write back to the sheet.
get target row's unique ID --> do work --> locate target rows ID and use to determine write range --> write back to sheet.
Alternatively, during the operation you could delete the target row and then use appendRow() to drop the updated version back in.
A third and final suggestion might be to temporarily suspend the permissions for the sheet. See: https://developers.google.com/apps-script/reference/spreadsheet/page-protection#setProtected