Sort with a button for a google spreadsheet - google-apps-script

I want a button in my spreadsheet which sorts based on 3 criteria.
I tried about 20 different ways of writing the syntax, but none of those worked.
I'd be glad if someone could tell me what is wrong.
function sort1() {
sort({C3:K12;9;FALSE;3;TRUE;8;FALSE})
}

If strict adherence to your predefined sorting criteria is not important, and you are happy to let users adjust the sort by themselves, you can achieve this result quite simply by selecting the region of data of interest, and then creating a "filter" on it.
A Google Sheets "filter" is really a rich sorting and filtering mechanism. Implemented this way, the end user can achieve the exact sort you mentioned by selecting the 3rd sort criterion (column 8 in your example), the 2nd (column 3), and then the 1st (column 9).
Example of a filter applied to a region of a sheet:

function sort1() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=sh.getRange(3,3,10,9);
rg.sort([{column:11,ascending:false},{column:5,ascending:true},{column:10,ascending:false}]);//The sort options is an array of objects but the array can be left for just one.
}
Reference

You can't run a Sheets function from an Apps Script like that. If you are writing the script yourself, you will need to get a range object and sort that. You may want to start by recording a macro, which will write the script for you (Tools > Macros). Then you can look at the script and see what it is doing. Once the script is working the way you want, here's a good post on running a script from a button: https://www.benlcollins.com/apps-script/google-sheets-button/

Since you plan on doing 3 types of sorting, you could use three buttons, each for the wanted type of sort. Then you could attach to each one the type of sorting, like presented below.
The global variables sheet and range.
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var range = sheet.getRange(3, 2, 11, 10);
The function sort1 sorts your data by points, descending:
function sort1() {
range.sort([{column:11,ascending:false}]);
}
The function sort2 sorts your data by games, ascending:
function sort2() {
range.sort([{column:5,ascending:true}]);
}
The function sort3 sorts your data by difference, descending:
function sort3() {
range.sort([{column:10,ascending:false}]);
}
Afterwards, in order to assign the above functions, you should create three buttons, like this:
After creating the buttons, you should assign to each one of the them the above functions:
SORT BY POINTS button -> sort1;
SORT BY GAMES button -> sort2;
SORT BY DIFF button -> sort3;
If you want to use all the sorting criteria in one instruction, you should just use all of them in the same sort method.
range.sort([{column:11,ascending:false},{column:5,ascending:true},{column:10,ascending:false}]);
Furthermore, I suggest you check the above links since they might be of help in your future development:
Sheet Class Apps Script;
Range Class Apps Script.

Related

Using named ranged in google sheets as variables in custom formulas

Not sure if this is going to make sense but here I go.
What I want to do is to create a formula that isn't linked to a cell directly. In example: if I want to calculate carryweight for a tabletop game like D&D I would need the formula (strengthBonus x 5). For my current attempt I renamed the range (cell rather) strengthBonus to MOD_STR so when I put the formula =(multiply(MOD_STR,5) it works like a charm. Then I named that range "CARRYWEIGHT" and then use it elsewhere.
What I would like to be able to do is to make a new variable, similar to the way that "Define Named Range" does, but instead of relying on the variables being somewhere on the spreadsheet they would process from an internal formula. For example, if I type =carryweight into a cell it would run the equation =MULTIPLY(MOD_STR,5) in that cell and output the answer. I know nothing about code yet but have just been pointed in the direction of tutorials but I'm also asking for help here.
The code I have tried is
function CARRYWEIGHT(MOD_STR){
return MOD_STR*2}
and something else, I can't remember what but I got it to at least accept it in the spreadsheet. When I type it in I get an error stating that the outcome isn't a number.
I have no idea where to go from here.
Thank you in advanced for your help.
The difference between sheets formulas and Apps Script is that in Apps Script you need to retrieve the value of the range corresponding to the name of a named range
You cannot simply multiply the name of the range (which is a string!) with a number
Here is a sample of how to retrieve a range by name and make calculations wiht the value stored in it:
function CARRYWEIGHT(MOD_STR){
// retrieve all named ranges in the spreadsheet
var namedRanges = SpreadsheetApp.getActive().getNamedRanges();
//loop through all the results
for (var i = 0; i < namedRanges.length; i++){
var range = namedRanges[i];
//if the range with the name equal to the value of MOD_STR is found, get the cell content of this range
if(range.getName()==MOD_STR){
var value = range.getRange().getValue();
// perform the calculation with the cell content of the named range
return value*2;
}
}
}
From the cell, call the function as =CARRYWEIGHT("paste here the name of the range of interest"), do not forget the quotes (unless it is a cell reference)!
I hope this helped you to get started, for further understanding plese consult the following references.
References
Named Ranges
Loops
Conditional statements
Ranges
getValue()

How do I keep values in 2 different sheets in sync?

I have a sheet that has some dates on it, like "25/02/2016", listed down a column. On the cells to the right of each date, there are some numeric values.
I need to copy these numeric values to a specific range on a different sheet. Ideally, it would work like a one-way sync, where I would write values on sheet1 and sheet2 would automatically be updated.
I've been looking through the Google Apps Script documentation, but I have no idea where to start. I do have some pseudo-code, just don't how to use it here.
function getDates() {
for (count = 0; count < sheet1.length; count++) {
if (hasDate) {
return(cell);
}
}
}
var numericValuesRows = sheet1.getDates().getRow();
var numericValuesRange = numericValuesRows.selectColumns(C-F);
Just making up method names and syntax. This bit is supposed to find out which rows have dates in them, and then select columns C to F in those rows. The next one is supposed to select the destination as all the cells from row3:columnC to row10:columnF, and copy the previously selected values to there.
var outputRange = sheet2.cellRange(C3-F10);
numericValuesRange.copyTo(outputRange);
I realise it's really crappy pseudo-code, but I'm hoping it at least helps in some way get across what I want to do. What would be the best way to do this?
Use the onEdit() trigger which will trigger when you edit a sheet. You can check the source of the edit to make sure its an edit to the cells you want in a particular sheet. Once that's done, its a matter of using getValue(), setValue() and getSheetByName(). Start here: simple triggers

Change order of columns spreahseet

I just want to confirm if there is any inbuilt function is google apps script to reorder the columns in the spreadsheet or do I have to do something like this -
var r = currentSheet.getRange(1, 1, currentSheet.getLastRow() - 1, currentSheet.getLastColumn()).getValues();
for(var i1=0, dLen=r[0].length; i1<dLen; i1++) {
//something to reorder
}
Please help!
it depends. if you just want to present the data in a different order, just use the built-in "=query" cell function, selecting the rows in the order you want.
query has some limitations thou. for example each column must have a consistent type on all rows (date, number, string etc). there arr other built-in functions that do similar things.
if you actually want to change the source range, you need to do it by getting the entire range array values, transforming it (possibly needing to take care of functions too) and writting the entire new array in a single range write.

How to evaluate a spreadsheet formula within a custom function?

In a spreadsheet I can enter =SIN(45)+123 in a cell, and it will be evaluated.
How can I evaluate spreadsheet functions within a custom function, something like an "eval"
function that would work like this :
function myFunc() {
return Sheet.eval("=SIN(45)+123")
}
is it possible ?
Note that I don't care about the SIN function in particular, what I want is to have access to the complete arsenal of spreadsheet functions (PMT, QUERY, NPER, etc..)
Spreadsheet functions from Apps-Script
Not possible - This has been asked many times. Suggest you check the google-apps-script issue list to see if anything has changed. But last I checked, there is no way to do it, and they have no plan to add it. https://code.google.com/p/google-apps-script-issues/issues/list
Ethercalc - java script spreadsheet formulas
If you need to, you can always copy the code from "ethercalc" as it has a java script versions of the spreadsheet formulas.
https://github.com/audreyt/ethercalc
I know this is an old question, but it might help someone.
just assign the formula to a range, then grab the value.
//asign a formula to the range
var range = sheet.getRange("A1").setFormula("=SUM(D1:D100)");
//get the result
var result = range.getValue();
//clean up
range.setFormula("");
I got this working. Using set value will do the trick. Thus something like this:
function MyFun1(){
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange('A1').setValue(Myfun2())
}
function MyFun2(){
return "=SIN(45)+123"
}
Hope this helps!
I think you need to divide this issue up into two different concerns.
Do you want to grab data that is already on the spreadsheet, perform a calculation, and then print a result, or do you want to use the sin() function on calculations in code unrelated to the data in the spreadsheet?
If you are trying to do the latter, you should be able to reference spreadsheet functions by using Math.sin() in your Google Apps Script. For more information on using the sin() function in JavaScript, check this post out: http://www.w3schools.com/jsref/jsref_sin.asp
If you are trying to do the former, then what you should do is use a readRows() function (more information available here: http://gassnippets.blogspot.com/2012/11/create-your-first-google-apps-script.html) to load your spreadsheet data into a variable (or variables) in memory, perform your calculations, and print the final result out to the spreadsheet using a similar function.
Let me know if this helps.
I came across this question in an attempt to find a way to evaluate part of a function like it is possible in Excel.
Here is my dirty workaround - instead of outputting the result in an msgbox, you could simply store the value or displayvalue of the activecell in a variable and use it to your liking.
Notice however, that the function will temporarily overwrite whatever you have in your currently selected cell and it will need to recalculate the sheet before the result is available. Hence it's not a viable solution if you need to evaluate multiple cell values.
function evalPart() {
var ui = SpreadsheetApp.getUi();
myPart = Browser.inputBox("Enter formula part:", ui.ButtonSet.OK_CANCEL);
if (myPart != "cancel") {
myActiveCell = SpreadsheetApp.getActiveSpreadsheet().getActiveCell();
myBackup = myActiveCell.getFormula();
myActiveCell.setFormula(myPart);
Browser.msgBox("Result of \\n \\n" + myPart + " \\n \\n " + myActiveCell.getDisplayValue());
myActiveCell.setFormula(myBackup);
}
}
I don't know if it's possible with high-level functions. However, it's possible with some common and easy-to-understand functions like (sum, subtract etc).
Following is the code I used to set values after the calculation is done in scripting itself.
function MyFun1() {
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange('A1').setValue(MyFun2());
}
function MyFun2() {
var one = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Dashboard");
var two = one.getRange('A2').getValue();
var three = one.getRange('A3').getValue(); return two*three;
}
Don't forget to add a trigger "onEdit" on Myfun1() to automatically update the return value.

Google script FIlter issue

I am programming a help desk system using google script, forms and spreadsheet.
To filter the queries the submissions are placed into different sheets depending on category, this is done through the FILTER function. however every time a new submission is made the filter function does not update, (it uses the CONTINUE function to cover the other cells)
instead the cell with the FILTER function must be selected and crtl+shift+E must be entered
is there a way around this?
I have tried two methods
the first was looking to have a function to enter the shortcut, but is this possible?
the second is auto entering the continue function everytime a new submission is made, I have this working however google sheets does not recognise the named range, (the continue function has the set up CONTINUE(original cell, rows away, columns away) its the original cell that it does not identify, instead I must manually select the cell and re-write the exact same cell reference.
Thank you for your help, if you need to see my code please ask :)
This is the code for the second option where I try to enter the function manually to the cells.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var numEntry = ss.getSheetByName('Home').getRange("B8").getValue() + 2;
var cat = ss.getSheetByName('Software problem').getRange(numEntry, 4, 1, 9);
cat.getCell(1, 1).setValue('=CONTINUE(D2, '+(numEntry-1)+', 1)');
Your option 1: Have a script enter keystrokes automatically? Not supported in apps-script.
Your Option 2: It shouldn't be necessary to programmatically insert CONTINUE, as the required CONTINUEs for your FILTER should be automatic, when rows in your filter range match the expressed criteria. Something else is wrong, so don't get caught up with this red herring.
You mention "google sheets does not recognise the named range" - I'd like to know what you mean by that, because I suspect this is where your solution will be. You can use named ranges within FILTER statements. You can also use open-ended ranges, like FormInput!A1:X or FormInput!E1:E.
If you're trying to manipulate named ranges using scripts, then you may have run into a known issue, "removeNamedRange() only removes named ranges that were created via Apps Script". (To get around that, manually delete the named range, then create it only from script.)
Here's a function I use to create a named range for all data on a sheet. You could adapt this to your situation. (I use this with QUERY functions instead of FILTER, you might want to consider that as an alternative.)
function setNamedRangeFromSheet(sheetName) {
// Cannot remove a named range that was added via UI - http://code.google.com/p/google-apps-script-issues/issues/detail?id=1041
var ss = SpreadsheetApp.getActiveSpreadsheet();
try { ss.removeNamedRange(sheetName) } catch (error) {};
var sheet = ss.getSheetByName(sheetName);
var range = sheet.getDataRange();
ss.setNamedRange(sheetName,range);
}
Using FILTER, you need to match the length of your sourceArray (which can be a named range) and any criteria arrays you use. To programmatically create a named range for a single-column criteria within your sourceArray, and of the same length, use getNumRows() on the sourceArray range.
Now, within your submission handling function, triggered on form submit, you'd have something like this. (I assume your trouble reports are coming into a single sheet, "FormInput" - adjust as necessary.)
...
var ss = SpreadsheetApp.getActiveSpreadsheet();
try { ss.removeNamedRange("FormInput") } catch (error) {};
var sheet = ss.getSheetByName("FormInput");
var inputRange = sheet.getDataRange();
ss.setNamedRange("FormInput",inputRange);
try { ss.removeNamedRange("Criteria") } catch (error) {};
var criteriaCol = 4; // Another guess, that Column E contains our criteria
var criteriaRange = sheet.getRange(0,criteriaCol,inputRange.getNumRows(),1);
ss.setNamedRange("Criteria",criteriaRange);
...
And with that in place, the content of A1 on your "Software problem" sheet just needs to contain the following. (Assuming that you're looking for "Bug"s.):
=FILTER(FormInput,Criteria="Bug")
I mentioned open-ended ranges earlier. If you aren't doing enough manipulation of data to justify named ranges, you could set up your filter like this, and not have to change it as new input came in:
=FILTER(FormInput!A1:X,FormInput!E1:E="Bug")