force empty cells to end of sorted column - google-apps-script

I want to sort a sheet by a column that has strings in it. I'm populating the sheet by putting a query based on an import range in the first cell. It seemed best if I did the sort as part of the query, order by Col6. This places all the empty rows at the top unless I sort descending, which I do not want to do. I found the opposite of what I want to do in the question titled "sort-empty-cells-to-top"
The answer there gets the values of the sheet's data range and works with the values array, then puts it back into the range. How would I change the compare function to force empty cells to the bottom in an ascending sort?

This might do what you want. You may have to build a helper column with somrthing in each row you want to return (like my Col7 (column G)).
=query(query(Sheet1!A1:G,"select * order by F "),"Select * where Col7 <>''")

Related

Can I show the results of COUNTIF in one column only if there is data in another column?

I have a spreadsheet with multiple sheets.
Sheet A is generated from a template, and it is where users will enter in data to be formatted into a report; it will be deleted after that report is submitted.
As people enter info into Column A of Sheet A, they don't have to enter client information if we already have them entered in this spreadsheet, but otherwise they need to go over to Sheet B and enter it in.
To make it easier for them (so they don't have to check every time, or go back and enter info later after getting an error generating the report), I have used conditional formatting. The way it works is, column Z is set up as a helper column, and uses a "COUNTIF" function to check if the client ID in Sheet A, Column A is found in Sheet B, Column C. Sheet A Column Z returns 0 or 1 (or, theoretically, more than one if we had duplicates), and then Sheet A column A has conditional formatting based on Column Z's value-- if the client is already in, the cell for client ID turns green after they type it; if not, it turns red.
It works great! However, I am adding scripts to these sheets, and looping through them. This helper column is filled from Z1 to Z1000, which means I can't use sheet.maxRow() to get the last row.
I see plenty of workarounds on the script side, but I was wondering if anyone has a clever way to input a value into the helper column Z ONLY IF the corresponding cell (same row) in column A has a value using spreadsheet formulas.
I suspect that an array formula with a filter might do it, but I have little experience with either and can't get anything to work out.
Thanks for your help!
You can use this expression with INDEX, MAX and ROW to make an ARRAYFORMULA only expandable until the last cell with value in column:
A2:INDEX(A2:A,MAX(ROW(A2:A)*(A2:A<>"")))
It will go from A2 to the maximum number of row in A in which A is different than null (that's why both conditions are multiplied).
Then you can set a formula like this in Z2 (check the ranges in case something is not right from reading your text, and delete all other formulas in Z too in case you weren't using an arrayformula already):
=BYROW(A2:INDEX(A2:A,MAX(ROW(A2:A)*(A2:A<>""))),LAMBDA(each,IF(each="","",COUNTIF('Sheet B'!C:C,each))))

Is there a way in google sheets to select a list of cells as an array to be used as a parameter in a connected sheet?

I have a new google sheet set up to query my database via a connected sheet.
The query returns a list of our shops and their sales per year. Each shop has an ID.
I am able to set Cell A1 in another, reference sheet, to be a parameter in the query. This way the connected query only returns results for that particular store ID.
When using this, I really want to put an IN function into my query. The connected query would then look something like.
SELECT * FROM shops where shops.id in (#RANGE)
And #RANGE would be A2:A as an array.
I've had success naming each cell as a new parameter and then:
SELECT * FROM shops where shops.id in (#REFERENCE1, #REFERENCE2)
Is there a more elegant solution?
Maybe a little late, but the easiest way I found was to convert to regex.
select (#POSTCODES) as test, postcode
from `postcode.au_towns`
where regexp_contains(#postcodes,safe_cast(postcode as string))
Where #POSTCODES is a gsheet string using a formula like join("|",UNIQUE(Sheet1!D2:D)).
Just make sure to remove the extra "|" generated using something like
left(B2,len(B2)-1)
This might work for you.
=SUBSTITUTE(QUERY(FILTER(D3:D,D3:D<>"",E3:E),"WHERE Col1 <> ''",9^99)," ","|")
This filters a column of store IDs based on which ones have been selected, and coverts that into a text string similar to the query you have been using. Producing something like "A1|A3|A7".
The query then just points to that result for the contains criteria.
Note that if your range of store IDs to report on is built in some other fashion, you just need to point to its range, instead of using the filter I have.
See a sample sheet here. This also shows a merged example of the two formulas, to produce the report all from one formula.
https://docs.google.com/spreadsheets/d/11uMa7CNcTXBnnpWGSIC_WvGSa-P2GTLQ2T7GvTgY4oM/edit?usp=sharing
Let us know if this helps you.
=QUERY(IMPORTRANGE("Google_Sheet_ID_Can_Be_Find_In_URL", "Sheet_Name!Range(you want to query)"),"SELECT * ")
or
=QUERY(IMPORTRANGE("Google_Sheet_ID_Can_Be_Find_In_URL", "Sheet_Name!A2:A"),"SELECT * ")
or
=QUERY(IMPORTRANGE("Google_Sheet_ID_Can_Be_Find_In_URL", "Sheet_Name!Range"),"SELECT * WHERE Col2='shops.id'")")
IMPORTRANGE() method import data from another worksheet. In the parameter, you type google sheet id from the url with quotes, type the desired sheet name end with ! Then you type the range from that sheet you want to query. When you wrap it with the outer QUERY() method, you can query the data from that range such as A2:A by selecting specific columns including the column with the range or * from that sheet name
When you're using IMPORTRANGE() method, it's going to return an array. The selected columns have to label in numeric like "SELECT Col 1, Col 2, Col 3"

Find Row Where Sum is Reached from Single Joined Column (not a range of cells)

I'm trying to run a formula to identify in which row a total sum is reached.
I've been able to do that calculation when I have an entire range of cells to work with, however, I'm doing a filter / join calculation because I need to do this from an individual row with all the data instead of an entire range of cells.
Here is an example google sheet (EDITABLE - feel free) where you can see the range and working formula (both below). Help getting this from the single-cell versions on the top would be very helpful. The error I get with both row() & index() formulas is that the "argument must be a range".
If there's another way to do this besides the single-cell I had that doesn't require referencing the range (e.g. using FILTER) then I'm open to it.
My desired result is to be able to pull the get the second column (date) at the point when the sum is reached (can be via the INDEX & MATCH formula I used or an alternative). This will tell me the earliest date that feeds into the desired sum.
Yes unfortunately you can't do that trick with SUMIFS to get a running total unless the column being totalled is an actual range.
The only approach I know is to multiply successive values by a triangular array like this:
1 0 0 ...
1 1 0 ...
1 1 1 ...
so you get just the sum of the first value, the first 2 values, then 3 values up to n.
This is the formula in F5:
=ArrayFormula(match(E14,mmult(IF(ROW(A1:INDEX(A1:ALL1000,COUNT(split(A5,",")),COUNT(split(A5,","))))>=
COLUMN(A1:INDEX(A1:ALL1000,COUNT(split(A5,",")),COUNT(split(A5,",")))),1,0),TRANSPOSE(SPLIT(A5,",")))))
And the formula in F6 is just
=to_date(INDEX(TRANSPOSE(SPLIT(B5,",")),F5,1))
EDIT
You might have guessed that the above formula was adapted from Excel, where you try to avoid volatile functions like Offset and Indirect.
I have realised since posting this answer that it could be improved in two ways:
(1) By using Offset or Indirect, thus avoiding the need to define a range of arbitrary size like A1:ALL1000
(2) By implying a 2D array by comparing a row and column vector, rather than actually defining a 2D array. This would give you something like this in F5:
=ArrayFormula(match(E14,mmult(IF(ROW(indirect("A1:"&address(COUNT(split(A5,",")),1)))>=
COLUMN(indirect("A1:"&address(1,COUNT(split(A5,","))))),1,0),TRANSPOSE(SPLIT(A5,",")))))
which could be further simplified to:
=ArrayFormula(match(E14,mmult(IF(ROW(indirect("A1:A"&COUNT(split(A5,","))))>=
COLUMN(indirect("A1:"&address(1,COUNT(split(A5,","))))),1,0),TRANSPOSE(SPLIT(A5,",")))))

Double Column lookup for Outline Format Data

So I am wondering how to find a specific cell in a given Column (in this case column F), when I want the search area for the lookup to be within a certain range, and these ranges will be separated when a new value shows up in column A (because the format for this data is in outline format). I know the description sounds confusing, the picture provide a better explanation than I can. The file format is excel, but I can convert to sheets if necessary.
So ideally what I want is either a query for some other lookup function that will search the sheet for column A first, so in this case I would search for title contains "ADC-BOH" and then do a sub-search within the area between the two rows with the long column A values (Totals for Dept...) for the Reg Hours. Then repeating that for OT, ESICK, etc.
I don't know how to concatenate a column A value (ADC-BOH) to the Column C value (Reg) to then get the value at column E, and make this dynamic for all of the respective restaurants (Column A) I have to do that are all on this sheet.

Move data to bottom of sheet

I've got a list of dates, clients, and shift data in a spreadsheet (rows A5:D34). I want to write this data to the last row of the sheet. Unfortunately, the number of rows is variable. On one week, the data may be 17 rows; later, it may be 23 rows.
I want to "archive" the data by moving the used rows to the bottom of the sheet.
I'm looking for a code (google script) solution that can work with the fact that the range to be copied is always 4 columns wide, but a variable number of rows.
Thanks for all your help!
This is a quite common problem where we might not know or have to manually include the total number of rows in the sheet. We can use, getRange() with last row values or getDataRange() to extract the range.
getRange(start_row,start_column,number_of_rows,number_of_columns);
to use the last row in the above syntax starting from A1, simply change the parameters as below.
var range = getRange(1,1,sheet.getLastRow(),sheet.getLastColumn());
The second option is to use getDataRange() which is
var range = sheet.getDataRange();
PS : Mind that range is a 2D array.
Hope this helps :)
I would suggest using sheet.getDataRange() to know how many rows and columns contain data. Once you know this you can derive the bottom row.
To quickly go through the returned values, I would suggest using sheet.getDataRange().getValues() which will return a 2 dimensional array with all the values stored in your sheet.
To know the last row containing data you can then easily do the following:
var lastRow = sheet.getDataRange.getValues().length;
Note that since the length of an array starts counting at 0 for the first row whereas when you want to set values back to your sheet your row count will start at 1. This means you need to add 1 to the lastRow count.
Now that you know the last row you can do some cleanup first to remove a previous bottom row with sheet.deleteRow(lastRow+1);
To then add a new bottom row you can simply use the append function where you pass an array with all the values for all cells of the row like this:
sheet.appendRow(["cellInColumnA";"cellInColumnB","cellInColumnC"]);