How do I efficiently output to a non-contiguous range in Google Apps Script (GAS) - google-apps-script

I'm new to Google Script, so appreciate your help!
Here's what my data looks like (3 non-contiguous records, 4 non-contiguous fields):
https://docs.google.com/spreadsheets/d/18FFB2HlcfcciHj7NPmihZbuf47op2UMdRTKfpyTqowU/edit#gid=0
I have an array of the items and each item is an object that contains 4 keys. I want to output to Google Sheets in as few SetValue requests as possible. If I can't do it in 1 call, then it makes most logical sense to output each item at a time.
My idea is that I can create ranges for fields 1 and 4 that span the entire column. Then I can create ranges that span the entire Item row. Then the INTERSECTION between the 2 is the range I want to output to, once I have assembled an array of 2 values.
Or perhaps since assuming I know the rows/columns of each cell below, I can return the range and the use a UNION of the ranges to create the mapping instead.
But is there a function to do Intersection or Union in GAS? Or am I better off just outputting each cell 1 by 1?
Thanks for your help!

There's no such function. It's either one by one or contiguous cells.
But there's quite a few tricks/alternatives that might work for you. As Sandy pointed in a comment, you could get a contiguous range that had all required cells and set the non-required ones to blank or their original values. This has the down side of not working for formulas.
You could also pre-configured the required cells with simple formulas "pointing" to a continuous range. That would simply bulk update at once.
If bulk setting is really required, you could also grab all values and formulas of the wide range, and convert all plain values to their equivalent formulas, e.g. abc becomes ="abc" and 1/1/2015 =DATE(2015,1,1) (yes, it's cumbersome) and then use setFormulas to set everything back, both original formulas and values converted to formulas. Making no actual content change in the cells you don't want to and changing the required ones, all in one bulk operation.
Anyway, these are just workarounds. As stated in the first sentence, it's not possible, period. You probably want to star this report in Apps Script issue tracker to kind of vote for this feature and receive updates.

Related

How can I reduce google sheets lag by replacing thousands of cell by cell formula calculations with an elegant script?

first time posting, so bear with me.
I have successfully designed a google sheet that automatically creates a crop map based on the crop plan in another tab ('Successions')
In 'Successions' the following columns are relevant:
Column A - Succession ID
Column C - Row ID
Column R - Planting Date
Column W - Harvest Date
In "Map 1A" I have created a map of our field, with Column B representing the Row ID and the columns to the right each representing a week of the year, whose start dates are defined in row 2.
My goal is to map each succession on the appropriate row for the duration that it will be in the ground (Planting Date to Harvest Date). I will have different successions occupying the same row during different date ranges.
I accomplished this in three steps:
Step 1
Every cell (except the left most column) contains a formula that returns and text joins (if multiple results) any successions in that row during that week - if an only if the value it would return is different than the value the cell to left would return. The result is that the succession ID is only displayed on the week that it starts. The formula in these cells is:
=Iferror(If((Textjoin(" / ",True,FILTER(Successions!$A$2:$A$647,RegExMatch(Successions!$C$2:$C$647,$B3),Successions!$R$2:$R$647<D$2,Successions!$W$2:$W$647>D$2)))=(Textjoin(" / ",True,Iferror(FILTER(Successions!$A$2:$A$647,RegExMatch(Successions!$C$2:$C$647,$B3),Successions!$R$2:$R$647<C$2,Successions!$W$2:$W$647>C$2),""))),"",(Textjoin(" / ",True,FILTER(Successions!$A$2:$A$647,RegExMatch(Successions!$C$2:$C$647,$B3),Successions!$R$2:$R$647<D$2,Successions!$W$2:$W$647>D$2)))),"")
The code for the leftmost column, which I did not want to check the cell to the left is:
=Iferror((Textjoin(" / ",True,FILTER(Successions!$A$2:$A$647,RegExMatch(Successions!$C$2:$C$647,$B3),Successions!$R$2:$R$647<C$2,Successions!$W$2:$W$647>C$2))),"")
Step 2
Because every cell contains a formula, I could not get the succession ID to spill into the adjacent cell and thus be readable. To solve this, I have an adjacent tab called "Map Final" that mirrors "Map 1A" via an Array Formula. Because it does not carry over formulas just values, it allows text to spill over.
Step 3
Conditional formatting is applied to "Map Final" via the Custom Formula function. There will be a rule for each crop type. Each succession is automatically colored the color of its crop type, and the color fills all cells for the duration of the succession.
My question is this:
How could I accomplish this same mapping in a more efficient way? Currently any update to Successions takes 30 seconds to a minute to update. The progress bars are killing me and basically make this really cool tool unusable for crop planning purposes, during which we are going back and forth from data to map frequently to make placement decisions.
Do you think this is possible using a custom script that pushes data rather than pulling data?
Would it render faster, or is the way I'm doing it the most efficient way?
While that is my main question, I am certainly open to any advice you have for improving speed by simply refining my current method.
Thanks in advance for any help you can provide!

Imported range time values dont match source range time values Google sheets script

I have simple google spreadsheet with two sheets: sheet1 and sheet2. In sheet2 there are time values in column A which I am trying to copy to column A in sheet2. I wrote a script(please see link) for it but values are not the same. What class/method do I need to use in order to get same values in both sheets.
I noticed if I format column A values in sheet2 to text than I get matching values but problem is I am not allowed to that and that column must remain formatted as time.
Also, my second question(please ignore if its off topic). If I have work schedule in google sheet for 3 employees: John,Peter and Mike and they all have editing permissions. If Mike decides to use filter to filter out just his work schedule, is it possible for other two employees to have unedited sheet(as I believe Mike's actions will affect other two persons). What I mean is, is it possible to restrict one persons actions just to view he is looking at while other have unaffected version(together work schedule in this case)?
link for spreadsheet:
https://docs.google.com/spreadsheets/d/1tOtwzM0CxDHBXzC8ECeXlfaTGC4i7rH2XMYYszqRINM/edit?usp=sharing
Much appreciated.
not entirely sure what the problem is, your script is working as intended and you have the same values in sheet1 as in sheet2. The only difference is that in Sheet1 you have a date format and on sheet2 you only indicate the time. I have changed the number format in the sheet1 and as you can see it's the same.
The problem you are having is that by indicating only the time, you let the sheet assume any date. For time it's not that big a deal, but you must be sure that time formats are the same. use getNumberFormat() and setNumberFormat() (read here) to set the correct formats when copying or have the formatting set in advance
As for filtering — you can simply give them view-only access and let them use filtere views (right under "Filter"). A filter will hide information for everyone on the sheet, while a filter view will only be visible to that person. You can also pre-set filter views for them as those with edit access can save filter views.

Merge two ranges into one range using query function in Google Spreadsheets

Please refer to my sample spreadsheet.
Basically this is for cleaning up form responses. There are two sets of identical questions in two different sections, results in two ranges. I wanted to merge them into one while retaining their order, dynamically.
=query({sheet1!A2:F999;sheet1!G2:L999},"select * where Col1 <> ''",0)
This is my try which merged the two ranges in sheet1 but it pulled data from range A2:F999 first, then from G2:L999, messing up the order. Is there any other way to merge them?
you can simply use if, in my opinion, as such
=arrayformula(if(isblank(sheet1!A2:F),sheet1!G2:L,sheet1!A2:F))
Btw, you don't need to use ranges as A2:F999. If you want to say "until the end of file" you can omit the row numbers, and simply use it as A2:F

Is there some way to populate all the cells from 1 function call instead of 1 call for each cell?

Here is my issue. I have a spreadsheet with multiple sheets, and each sheet has about 300-500 rows. I am using ScriptDb to store the data for each sheet.
What I am currently doing is calling a custom function in 300-500 cells in each sheet to populate certain cells with data, and what happens is that some will populate and the rest will error out saying i've queried the database too many times in a short period. Obviously having to query the database for each cell isn't the best solution.
How would I go about querying all the data for the current sheet and then having that data available to grab for each cell. What I've read is that you can't really have "global" variables in GAS, but have to use things such as CacheService or ScriptDB, which is what i'm trying to do. I'm just querying it too much.
Is there some way to populate all the cells from 1 function call instead of 1 call for each cell? What am I missing or what other solutions are there?
Just realized a similar question was asked earlier today: Google Spreadsheet Script invoked too many times per second for this Google user account
Yes its possible. Simply return an array from your function. It will work like an arrayformula.
Of course your cells would need to be contiguous.

Extracting Values from 300 sheets where vlookup doesn't work

I have an excel spreadsheet which has just under 300 sheets in it. The first sheet has a list of items which are all numbered 1.1.1, 1.1.2 etc. The rest of the sheets have some of the items listed on them and not in numerical order. I am trying to extract the quantity and total listed against these items on all the different sheets.
The sheets are complicated by the fact that they are not well structure so have section titles which are across merged cells.
I could get this information by hand using the search facility in excel and visit each instance of the number and then add up all the quantities and totals by hand. Is there any way I can automate this? i.e. by asking excel to take each unique identifier from sheet 1, find it in the rest of the sheets and return the quantity and/or total?
I tried using vlookup but it only seemed to return one of the values and ignore all the others.
Even if there was a formula that I had to change the unique identifier by hand that would be much quicker!
Thank you for any help you can give. I am not a programmer so constructing the vb by myself would probably take longer than doing it by hand!
If you add a $ before your lookup data for example
search - $A&2:&AZ&1000
then you are telling excel to look for anything in that array.
because if you do not have a $ in and you copy it, it starts excluding everything above that.