I want to make a function in google sheet, for example here "sum" I want it sum all above cells until the previous another function
So if I copied it to another row it will sum all above cell until the previous function also (3 of pic).
Try these custom functions
// mike steelson
function sumSinceLastFormula(rng){
var lastRow = SpreadsheetApp.getActiveRange().getRow()-1
var col = SpreadsheetApp.getActiveRange().getColumn()
var sum=0
for (var i = lastRow; i>1; i--){
var value = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(i,col).getFormula()
if (value && value.toString().charAt(0) === '=') {break}
else {sum += SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(i,col).getValue()}
}
return sum
}
function countaSinceLastFormula(rng){
var lastRow = SpreadsheetApp.getActiveRange().getRow()-1
var col = SpreadsheetApp.getActiveRange().getColumn()
var counta=0
for (var i = lastRow; i>1; i--){
var value = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(i,col).getFormula()
if (value && value.toString().charAt(0) === '=') {break}
else {counta++}
}
return counta
}
function countifSinceLastFormula(rng,crit){
var lastRow = SpreadsheetApp.getActiveRange().getRow()-1
var col = SpreadsheetApp.getActiveRange().getColumn()
var countif=0
for (var i = lastRow; i>1; i--){
var value = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(i,col).getFormula()
if (value && value.toString().charAt(0) === '=') {break}
else {if (SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(i,col).getValue()==crit) {countif++} }
}
return countif
}
to automatically update the values, add reference to previous cells
=sumSinceLastFormula(F$2:F8)
when in F9, and copy where you need it.
https://docs.google.com/spreadsheets/d/1iXDbYDd_5rmHa1E41zobTWB6MKvABR1ERpCgcValIng/copy
You can calculate the cumulative by a single formula like
={"sum";arrayformula(SUMIF(ROW(A2:A),"<="&ROW(A2:A),F2:F))}
Basically I have two google sheets that look something like this:
a table where people can put in their email and select what kind of foo they are using
email
foo
example#email.com
This Foo
and then another table with information about the foo
foo name
foo type
foo boolean1
foo boolean2
This Foo
String
True
True
That Foo
Number
False
True
Other Foo
String
False
False
In a Separate Sheet I'd like to have a dashboard-like view of things wherein I would have counts of various things like number of people, how many of each type of Foo, etc
Where I'm having trouble is figuring out how to pull things like "Number of people who have selected String foos" and such
like, basically i want the google-query equivalent to (in sql)
SELECT COUNT(p.*) FROM people p JOIN info i on p.foo = i.foo_name GROUP BY i.foo_type WHERE i.foo_type = 'String'
What I would be looking for is a table that looks like this:
Data
Count
Active Roster
4
String
3
Number
1
I have also seen many solutions that have complicated formulas using VLOOKUP, INDEX, MATCH, etc.
I decided to write a user function to combine tables, or as I refer to it, de-normalize the database. I wrote the function DENORMALIZE() to support INNER, LEFT, RIGHT and FULL joins. By nesting function calls one can join unlimited tables in theory.
DENORMALIZE(range1, range2, primaryKey, foreignKey, [joinType])
Parameters:
range1, the main table as a named range, a1Notation or an array
range2, the related table as a named range, a1Notation or an array
primaryKey, the unique identifier for the main table, columns start with "1"
foreignKey, the key in the related table to join to the main table, columns start with "1"
joinType, type of join, "Inner", "Left", "Right", "Full", optional and defaults to "Inner", case insensitive
Returns: results as a two dimensional array
Result Set Example:
=QUERY(denormalize("Employees","Orders",1,3), "SELECT * WHERE Col2 = 'Davolio' AND Col8=2", FALSE)
EmpID
LastName
FirstName
OrderID
CustomerID
EmpID
OrderDate
ShipperID
1
Davolio
Nancy
10285
63
1
8/20/1996
2
1
Davolio
Nancy
10292
81
1
8/28/1996
2
1
Davolio
Nancy
10304
80
1
9/12/1996
2
Other Examples:
=denormalize("Employees","Orders",1,3)
=denormalize("Employees","Orders",1,3,"full")
=QUERY(denormalize("Employees","Orders",1,3,"left"), "SELECT * ", FALSE)
=QUERY(denormalize("Employees","Orders",1,3), "SELECT * WHERE Col2 = 'Davolio'", FALSE)
=QUERY(denormalize("Employees","Orders",1,3), "SELECT * WHERE Col2 = 'Davolio' AND Col8=2", FALSE)
=denormalize("Orders","OrderDetails",1,2)
// multiple joins
=denormalize("Employees",denormalize("Orders","OrderDetails",1,2),1,3)
=QUERY(denormalize("Employees",denormalize("Orders","OrderDetails",1,2),1,3), "SELECT *", FALSE)
=denormalize(denormalize("Employees","Orders",1,3),"OrderDetails",1,2)
=QUERY(denormalize("Employees",denormalize("Orders","OrderDetails",1,2),1,3), "SELECT *", FALSE)
=QUERY(denormalize(denormalize("Employees","Orders",1,3),"OrderDetails",4,2), "SELECT *", FALSE)
function denormalize(range1, range2, primaryKey, foreignKey, joinType) {
var i = 0;
var j = 0;
var index = -1;
var lFound = false;
var aDenorm = [];
var hashtable = [];
var aRange1 = "";
var aRange2 = "";
joinType = DefaultTo(joinType, "INNER").toUpperCase();
// the 6 lines below are used for debugging
//range1 = "Employees";
//range1 = "Employees!A2:C12";
//range2 = "Orders";
//primaryKey = 1;
//foreignKey = 3;
//joinType = "LEFT";
// Sheets starts numbering columns starting with "1", arrays are zero-based
primaryKey -= 1;
foreignKey -= 1;
// check if range is not an array
if (typeof range1 !== 'object') {
// Determine if range is a1Notation and load data into an array
if (range1.indexOf(":") !== -1) {
aRange1 = ss.getRange(range1).getValues();
} else {
aRange1 = ss.getRangeByName(range1).getValues();
}
} else {
aRange1 = range1;
}
if (typeof range2 !== 'object') {
if (range2.indexOf(":") !== -1) {
aRange2 = ss.getRange(range2).getValues();
} else {
aRange2 = ss.getRangeByName(range2).getValues();
}
} else {
aRange2 = range2;
}
// make similar structured temp arrays with NULL elements
var tArray1 = MakeArray(aRange1[0].length);
var tArray2 = MakeArray(aRange2[0].length);
var lenRange1 = aRange1.length;
var lenRange2 = aRange2.length;
hashtable = getHT(aRange1, lenRange1, primaryKey);
for(i = 0; i < lenRange2; i++) {
index = hashtable.indexOf(aRange2[i][foreignKey]);
if (index !== -1) {
aDenorm.push(aRange1[index].concat(aRange2[i]));
}
}
// add left and full no matches
if (joinType == "LEFT" || joinType == "FULL") {
for(i = 0; i < lenRange1; i++) {
//index = aDenorm.indexOf(aRange1[i][primaryKey]);
index = aScan(aDenorm, aRange1[i][primaryKey], primaryKey)
if (index == -1) {
aDenorm.push(aRange1[i].concat(tArray2));
}
}
}
// add right and full no matches
if (joinType == "RIGHT" || joinType == "FULL") {
for(i = 0; i < lenRange2; i++) {
index = aScan(aDenorm, aRange2[i][foreignKey], primaryKey)
if (index == -1) {
aDenorm.push(tArray1.concat(aRange2[i]));
}
}
}
return aDenorm;
}
function getHT(aRange, lenRange, key){
var aHashtable = [];
var i = 0;
for (i=0; i < lenRange; i++ ) {
//aHashtable.push([aRange[i][key], i]);
aHashtable.push(aRange[i][key]);
}
return aHashtable;
}
function MakeArray(length) {
var i = 0;
var retArray = [];
for (i=0; i < length; i++) {
retArray.push("");
}
return retArray;
}
function DefaultTo(valueToCheck, valueToDefault) {
return typeof valueToCheck === "undefined" ? valueToDefault : valueToCheck;
}
// Search a multi-dimensional array for a value
function aScan(aValues, searchStr, searchCol) {
var retval = -1;
var i = 0;
var aLen = aValues.length;
for (i = 0; i < aLen; i++) {
if (aValues[i][searchCol] == searchStr) {
retval = i;
break;
}
}
return retval;
}
You can make a copy of the google sheet with data and examples here:
https://docs.google.com/spreadsheets/d/1vziuF8gQcsOxTLEtlcU2cgTAYL1eIaaMTAoIrAS7mnE/edit?usp=sharing
I found two scripts. Since they both were an onEdit() function they couldn't work side by side, I tried to merge them.
I am using this sheet for a very basic inventory of things I sell / keep track of what I have sold.
I want two things;
First: I would like the cell in column A to change to the current date whenever I do a change on that row.
Second: If I change the value in column B to "Sold" I want it to be moved to a different sheet (as well as getting a new date due the change).
Column B has the following choices:
-Ja (as in stock)
-Bokad (as in booked)
-Såld (as in sold)
The name of the Sheets are:
-Blocket (inventory)
-Sålda (sold)
function onEdit(event) {
// assumes source data in sheet named Blocket
// target sheet of move to named Sålda
// test column with "Såld" is col 2
var s = event.source.getActiveSheet(),
cols = [2],
colStamp = 1,
ind = cols.indexOf(event.range.columnStart)
if (s.getName() == 'Blocket' || ind == -1) return;
event.range.offset(0, parseInt(colStamp - cols[ind]))
.setValue(e.value ? new Date() : null);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "Blocket" && r.getColumn() == 2 && r.getValue() == "Såld")
{
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Sålda");
if(targetSheet.getLastRow() == targetSheet.getMaxRows()) {
targetSheet.insertRowsAfter(targetSheet.getLastRow(), 20); //inserts 20 rows
after last used row
}
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
}
See if this works
function onEdit(e) {
var s, targetSheet;
s = e.source.getActiveSheet();
if (s.getName() !== 'Blocket' || e.range.columnStart == 1) return;
s.getRange(e.range.rowStart, 1)
.setValue(new Date());
if (e.range.columnStart == 2 && e.value == "Såld") {
targetSheet = e.source.getSheetByName("Sålda");
if (targetSheet.getLastRow() == targetSheet.getMaxRows()) {
targetSheet.insertRowsAfter(targetSheet.getLastRow(), 20); //inserts 20 rows
}
s.getRange(e.range.rowStart, 1, 1, s.getLastColumn()).moveTo(targetSheet.getRange(targetSheet.getLastRow() + 1, 1));
}
}
if the same value is duplicated in the particular column, i need the the same text to be pasted respective to the above cell in the same column.
Eg,
Column A1 has 1111 and column B1 has abcd , if 1111 duplicated in A2, abcd should be auto posted in B2.
Is is possible in conditional formatting???
Here's a script that does what I think you want.
function onEdit(e)
{
var sht=SpreadsheetApp.getActiveSpreadsheet().getSheetByName('ColumnADuplicates');
var rng=sht.getDataRange();
var rngA=rng.getValues();
var cell = e.range;
var row = cell.getRow();
var col = cell.getColumn();
var value = cell.getValue();
//Logger.log('row = ' + String(row) + ' , column = ' + String(col) + ' , value = ' + String(value));
var nextCell = sht.getRange(row, col+1);
if(col==1)
{
for(var i=0;i<rngA.length;i++)
{
if(rngA[i][0] == value && i+1 != row)
{
nextCell.setValue((rngA[i][1] !='undefined')?rngA[i][1]:'');
break;
}
}
}
}
Here's what my sheet looks like:
I have too many columns in the sheet and so many columns to hide as well, but while executing the script it runs half way and stopped saying maximum time reached.
When I again tried to execute it stopped exactly where I stopped previously. So I would like to have some customization that if the column is already hidden can skip that column and work on the others.
Is there any way to do it.
Here is the code I used:
function hideColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('ANALYTIC');
var data = sh.getRange('6:6').getValues();
var numCols = sh.getMaxColumns();
var numRows = sh.getMaxRows();
for(var i = 0; i <= numCols; i++){
if(data[0][i] == ""){
sh.hideColumns(i+1);
} else {
sh.unhideColumn(sh.getRange(1, i+1, numRows, 1));
}
}
}
Please help me.
You can use the documentProperties to store the last column before the end of the execution. To prevent the run from stopping abruptly you stop the run a little prematurely at 5min (execution will terminate at 6min mark) mark and store the column number in the documentProperty. You also display an alert asking you rerun the script.
Then retrieve the column number on the next run and start from there. If the program gets through the complete loop you delete the said properties. So you start from zero if you rerun the script next time.
Below is the code for the same
function hideColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('ANALYTIC');
var data = sh.getRange('6:6').getValues();
var numCols = sh.getMaxColumns();
var numRows = sh.getMaxRows();
var docProp = PropertiesService.getDocumentProperties()
var startCol = Number(docProp.getProperty("startCol")) //if there is no propert called startCol will return Null, Number(Null) = 0
Logger.log(startCol)
var startTime = new Date().getTime()
var ms5min = 5*60*1000 //5min in millseconds
for(var i = startCol; i <= numCols; i++){
if(data[0][i] == ""){
sh.hideColumns(i+1);
} else {
sh.unhideColumn(sh.getRange(1, i+1, numRows, 1));
}
var curTime = new Date().getTime()
var elasped = curTime-startTime
if (elasped >= ms5min){
docProp.setProperty("startCol", i)
SpreadsheetApp.getUi().alert("Please restart Run, exceeded 5min mark")
return
}
}
Logger.log(elasped)
docProp.deleteAllProperties()
}
If your sheet has under 10,000 columns (PropertiesService limit) you can use this script:
function hideColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('ANALYTIC');
var data = sh.getRange('6:6').getValues();
var documentProperties = PropertiesService.getDocumentProperties()
var documentPropertiesVals = documentProperties.getProperties();
var numCols = sh.getMaxColumns();
for(var i = 0; i < numCols; i++){
if (!(cPlusInt(i) in documentPropertiesVals)) {
documentPropertiesVals[cPlusInt(i)] === 'empty';
}
if (documentPropertiesVals[cPlusInt(i)] === 'hidden' && data[0][i] == "") continue;
if (documentPropertiesVals[cPlusInt(i)] === 'unhidden' && data[0][i] != "") continue;
if(data[0][i] == ""){
sh.hideColumns(i+1);
documentProperties.setProperty(cPlusInt(i), 'hidden')
} else {
sh.unhideColumn(sh.getRange(1, i+1, 1, 1));
documentProperties.setProperty(cPlusInt(i), 'unhidden')
}
}
}
function cPlusInt(num) {
return 'c'+num.toString()
}
You at first you may need to run this few times (many write operation to PropertiesService may be sluggish) but later it's "blazingly" fast (0.1 s per new column).
Better answer using #Jack Brown's idea
It is possible to make single save to PropertiesService - you would need to incorporate time limit from #Jack Brown's answer, this way:
function hideColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('ANALYTIC');
var data = sh.getRange('6:6').getValues();
var documentProperties = PropertiesService.getDocumentProperties()
var documentPropertiesVals = documentProperties.getProperties();
var numCols = sh.getMaxColumns();
var startTime = new Date().getTime()
var ms5min = 5*60*1000 //5min in millseconds
for(var i = 0; i < numCols; i++){
if (!(cPlusInt(i) in documentPropertiesVals)) {
documentPropertiesVals[cPlusInt(i)] === 'empty';
}
if (documentPropertiesVals[cPlusInt(i)] === 'hidden' && data[0][i] == "") continue;
if (documentPropertiesVals[cPlusInt(i)] === 'unhidden' && data[0][i] != "") continue;
if(data[0][i] == ""){
sh.hideColumns(i+1);
documentPropertiesVals[cPlusInt(i)] = 'hidden'
} else {
sh.unhideColumn(sh.getRange(1, i+1, 1, 1));
documentPropertiesVals[cPlusInt(i)] = 'unhidden'
}
var curTime = new Date().getTime()
var elapsed = curTime-startTime
if (elapsed >= ms5min){
break;
}
}
documentProperties.setProperties(documentPropertiesVals)
if (elapsed >= ms5min){
SpreadsheetApp.getUi().alert("Please restart Run, exceeded 5min mark")
}
}
cPlusInt function explanation
cPlusInt is necessary because of weird problems with google's PropertiesService. Object gotten from PropertiesService returns undefined at integer keys. To see problem use this code:
function test() {
var obj = {};
for (var i=0; i<10; i++) {
obj[i.toString()] = 'aa' + i.toString();
}
var documentProperties = PropertiesService.getScriptProperties();
documentProperties.deleteAllProperties();
documentProperties.setProperties(obj);
obj = documentProperties.getProperties();
Logger.log(obj)
for (var i in obj) {
Logger.log('%s %s %s', i, obj[i], i === '1');
}
}