Count and display how many times values changed in a column - google-apps-script

At the start sorry for my poor english, i will try my best to explain everything.
Is there a way to count how many times value in column D has been changed and show it in column E?
In this example in column D person can select two values from list - "☐" and "☑". In column E i would want to see how many times person changed their selection. Lets say for for E3 i want to see how many time D3 was changed, for E4 to show how many times D4 was change and so on.
Thank you for all help!
Example spreadsheet
The only thing i found was somehitng like that:
function onEdit(e) {
if(e.range.getA1Notation() == "D2") {
var sCounter = e.source.getRange("E2");
var counter = sCounter.getValue();
if(counter === 0) {
counter = 1;
} else {
counter ++;
}
sCounter.setValue(counter);
}
}
The problem is that it only counts one cell and i need it to work for the whole column.

This should do it:
function onEdit(e) {
var ss =SpreadsheetApp.getActiveSpreadsheet()
var s=ss.getActiveSheet()
var editColumn=e.range.getColumn()
var editRow = e.range.getRow()
if(editColumn == 4 && editRow >=2) {
var sCounter = s.getRange(editRow,editColumn+1,1,1);
var counter = sCounter.getValue();
if(counter === 0) {
counter = 1;
} else {
counter ++;
}
sCounter.setValue(counter);
}}
Here is my test spreadsheet you can copy and try:
https://docs.google.com/spreadsheets/d/1I5_NVMKPvqN9J2sSlcLRnQbzoVF4iRKOuXJ5mshb-RY/edit?usp=sharing

Related

Google Sheets, cell counter that counts every time a cells values change

I'm trying to get a counter going in google sheets that will count how many times a cell has changed or updated.
I want cell "K1" to count how many times cell "Client!A2" values have changed.
This is my first time using Apps-Script so I feel like I must be missing something here. This is what has been input into the code.gs section.
function onEdit(e) {
if(e.range.getA2Notation() == "Client!A2") {
var sCounter = e.source.getRange("K1");
var counter = sCounter.getValue();
if(counter === 0) {
counter = 1;
} else {
counter ++;
}
sCounter.setValue(counter);
}
}
It doesn't seem to be working,
Am I supposed to be putting anything else in the brackets? Or am I just doing it wrong altogether?
function onEdit(e) {
const sh = e.range.getSheet();
const shts = ['Sheet1','Sheet2'];//included sheets
const idx = shts.indexOf(sh.getName());
if(~idx && e.value != e.oldValue) {
let n = Number( PropertiesService.getScriptProperties().getProperty('editcounter'));//counter
PropertiesService.getScriptProperties().setProperty('editcounter',++n);
sh.getRange('K1').setValue(n);
e.source.toast(n);
}
}

Google onEdit Script for multiple columns drop downs

I have some script that allows me to select multiple items from a drop down in google sheets. I have the below script which works fine for column L(12) but I want this to also work on column M(13) and N(14). I have tried || (as in OR) which then makes all the drop downs in the sheet also allow multiple selections which I don't want. Can anyone help me with this?
function onEdit(e) {
var oldValue;
var newValue;
var ss=SpreadsheetApp.getActiveSpreadsheet();
var activeCell = ss.getActiveCell();if(activeCell.getColumn() == 12 && ss.getActiveSheet().getName()=="Client information"){
newValue=e.value;
oldValue=e.oldValue;
if(!e.value) {
activeCell.setValue("");
}
else {
if (!e.oldValue) {
activeCell.setValue(newValue);
}
else {
if(oldValue.indexOf(newValue) <0) {
activeCell.setValue(oldValue+','+newValue);
}
else {
activeCell.setValue(oldValue);
}
}
}
}
}
function myFunction() {
}
You need to make sure that those columns should be within the range of the statement as what dared provided as an answer. column should be within 12 and 14 inclusively.
Additionally, I have made some modifications to make use of the event object instead of SpreadsheetApp and improved your if-else block.
Code Modifications:
function onEdit(e) {
// Spreadsheet object can be retrieved from event via e.source
var sheetName = e.source.getActiveSheet().getName();
// Range object can be retrieved from event via e.range
var range = e.range;
var column = range.getColumn();
if (sheetName == "Client information" && column >= 12 && column <= 14 && e.value) {
var newValue = e.value;
var oldValue = e.oldValue;
// first if statement is the default behavior, no need to include
// the else statement is now combined on the outer if statement
if (!e.oldValue)
range.setValue(newValue);
else {
if (oldValue.indexOf(newValue) < 0)
range.setValue(oldValue + ',' + newValue);
else
range.setValue(oldValue);
}
}
}
Output:
function onEdit(e) {
// cache the column number
let col = e.range.getColumn()
if ( e.source.getActiveSheet().getName() === "Client information" &&
col > 11 && col < 15) {
// This block will run if any of the columns L, M or N are edited
// If you need different behaviour depending on which column was edited test the column number
// Column L
if (col === 11) {
// This block will run if column L is edited
}
}
// Without seeing the type of data you are working with I'm unclear on what
// your old and new values represent.
}

Build Google App Script to Combine Similar Rows into One Row

Many people are asking why I want to do this. I want to do this so that when I do my mail merge (this sends students their overdue book lists from the library), I don't send a student an email more than once. I never use this data more than once, I only use it to send a quick message, I never manipulate or work with the data so I don't care if it's hard to work with! I hope this makes sense! Thank you for your feedback thus far.
Google Sheet starts like this:
I want it to look like this:
I have started some script, which I am sure you will all laugh at (I know very little of programming). However, It'd be awesome to be able to do this. Basically, combine the rows that have an identical entry in column 1, by putting the values for the columns for those rows together into one row. The numbers can be added, and the data separated by a comma or a line break.
This is what I have so far....
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var last = sheet.getLastRow();
//find identical entries in column 1//
for(i in data){
var row = data[i];
var duplicate = false;
for(j in newData){
if(row.join() == newData[j].join()){
duplicate = true;
}
}
if(!duplicate){
newData.push(row);
}
}
//add information from rows of identical entries into one row using a comma//
//delete empty rows//
}
It's a bit of a kluge but it runs with my fake data. Hopefully you can get it running with yours. It usually takes me some tweaking to get something like this a little more streamlined. I have a few comments in there to explain some of the key items.
function rowMerge() {
var firstRow = 2;
var firstCol = 1;
var sht = SpreadsheetApp.getActiveSheet();
sht.getRange(firstRow, firstCol, sht.getLastRow() - firstRow + 1, sht.getLastColumn() - firstCol + 1).sort(1);
sht.appendRow(['***','***','Control-z one more time','***','***']); //need a throwaway row to get last data element out since I'm moving out element[i-1]
var datR = sht.getDataRange();
var lastRow = datR.getLastRow();
var lastCol = datR.getLastColumn();
var datA = datR.getValues();
sht.getRange(2,1,lastRow - firstRow + 1,lastCol - firstCol + 1).clearContent().setWrap(true);
var datoutA = [];
var k=1;
var n = 0;
for(var i = 0;i < datA.length; i++)
{
if(i > 1)
{
if(datA[i][0] == datA[i-1][0])
{
k++; //k is the number of consecutive matching values
}
else
{
datoutA[n] = [];
if(k == 1)
{
// if k = 1 the datA[i-1] row gets copied into output array
for(var c = 0;c < datA[i-1].length; c++)
{
datoutA[n][c]=datA[i-1][c];
}
}
else
{
//i-1 to i-k rows get merged and copied into output array
var firstTime = true;
for(var a = 1;a <= k;a++)//input rows
{
for(var b = 0;b < datA[i].length -1;b++)//input columns
{
if(a > 1 || b > 0) //no delimiter for first row or first column
{
datoutA[n][b] += ', ';
}
if(firstTime || b == 0)// straight assignment for first row and running sum after that same with first column because we only want one of them because they're all the same.
{
datoutA[n][b] = datA[i - a][b];
}
else
{
datoutA[n][b] += datA[i - a][b];
}
}
if(firstTime)//first assignment then running sums for last column
{
datoutA[n][datA[i].length - 1] = Number(datA[i - a][datA[i].length-1]);
}
else
{
datoutA[n][datA[i].length - 1] += Number(datA[i - a][datA[i].length-1]);
}
firstTime=false;
}
var end = 'is near';
}
k=1; //consecutive counter
n++; //datoutA index
}
}
}
var datoutR = sht.getRange(2, 1, datoutA.length , datoutA[0].length);
datoutR.setValues(datoutA);
var colwidth = 250;
sht.setColumnWidth(2, colwidth);
sht.setColumnWidth(3, colwidth);
sht.setColumnWidth(4, colwidth);
}
There actually are many reasons why one would want to combine similar rows.
I for one, have a form that allows users to fill out information about properties. New info comes in everyday, so the forms will be reused for new entries.
All properties have a unique identifier, but the first form entry is useful to give the owner's name and address, along with other information.
The next form entry does not need to populate, (and re-type) all that info, but it does need to add other new details to the first entry.
Lastly, I want to report based on unique entries, with other info combined to read with the property.
Part of the answer for me is a custom function created by Hyde, called JoinRows.
Search for that.
Good luck!

Setting timestamp in first empty cell of column

I have not been able to find this anywhere. I'm guessing I'm searching for the wrong keywords. So far I have been able to use:
function getFirst() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var column = spr.getRange('F2:F260');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct][0] != "" ) {
ct++;
}
return (ct);
}
But I can't quite figure out how to set the return value to (new Date()).
This:
function clockOut() {
SpreadsheetApp.getActiveSheet().getRange('F2').setValue(getFirst(new Date));
}
Sets the time based off of the ct which, because it's zero, looks like: | 12/31/1899 0:00:00 |. Also in the F2 spot not in the ct spot. I think I'm close yet feel like I'm going in the wrong direction. Thanks
You were not too far in the first part... after that it becomes a bit confuse... here is a working example, I changed it to allow for easy customization on choosing the column.
function clockOut() {
var sh = SpreadsheetApp.getActiveSheet();
var col = "F";// choose the column you want
sh.getRange(col+getFirstEmptyCell(col)).setValue(new Date());
}
function getFirstEmptyCell(col) {
var sh = SpreadsheetApp.getActiveSheet();
var values = sh.getRange(col+"1:"+col).getValues()
var ct = 0;
while ( values[ct][0] != "" ) {
ct++;
}
ct++;// because we want to get the row after the last non-empty row
Logger.log(ct);// just to check
return (ct);
}

Vlookup or Indexing using google apps scripts

I have a spreadshseet (Sheet1) in which the Data is there from Col A to Column D, and in another sheet (Sheet1), again the data is there from Col A to Col W, in which the Col F data has some matching with column D.
What i am seeking for:
I want to pull data from Sheet2 (from Col F onwards, i.e. G, H, I etc.) in Col E and so on in Sheet1.
Sheet2
Col
F G H I J K L
1 A B C D E F
2 a1 b1 c1 d1 e1 f1
3 a2 b2 c2 d2 e2 f2
and so on
Sheet1
Col D E F G H
1 A B C D
3 a2 b2 c2 d2
Data to reflect in col E,F,G H in sheet1 from sheet2 against column D in col E,F, either using vlookup, or indexing.
What i tried but in Vain
http://productforums.google.com/forum/#!topic/apps-script/HzeNdIqnIUc
I want to use only google apps only to get the desired results.
Requesting for help on this.
Regards
I'm sorry you're having a problem with your VLOOKUP but it's a Google Spreadsheets user question not a stackoverflow (programming) question. It might help you to know these things:
You're on the right track with VLOOKUP. The spreadsheet function VLOOKUP definitely works for getting data in sheet2 to lookup based on keys/columns in sheet1. It works really well for what you say you need to do.
You can search the Google support site: http://support.google.com/docs/bin/search.py?query=vlookup
You can ask in the Google Docs Help forum https://productforums.google.com/forum/#!forum/docs
Good luck.
Its an old article but others may still stumble across it.
Here is something I wrote to address my Vlookup Needs in script form.
//~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`
//--//Dependent on isEmpty_()
// Script Look-up
/*
Benefit of this script is:
-That google sheets will not continually do lookups on data that is not changing with using this function as it is set with hard values until script is kicked off again.
-Unlike Vlookup you can have it look at for reference data at any Column in the row. Does not have to be in the first column for it to work like Vlookup.
-You can return the Lookup to Memory for further processing by other functions
Useage:
var LocNum = SpreadsheetApp.openById(SheetID).getSheetByName('Sheet1').getRange('J2:J').getValues();
Lookup_(Sheetinfo,"Sheet1!A:B",0,[1],"Sheet1!I1","n","y");
//or
Lookup_(Sheetinfo,"Sheet1!A:B",0,[1],"return","n","n");
//or
Lookup_(Sheetinfo,"Sheet1!A:B",0,[0,1],"return","n","n");
//or
Lookup_(Sheetinfo,"Sheet1!A:B",1,[0],"return","y","n");
//or
Lookup_(Sheetinfo,"Sheet1!A:G",4,[0],"Database!A1","y","y");
*/
function Lookup_(Search_Key,RefSheetRange,SearchKey_Ref_IndexOffSet,IndexOffSetForReturn,SetSheetRange,ReturnMultiResults,Add_Note)
{
var RefSheetRange = RefSheetRange.split("!");
var Ref_Sheet = RefSheetRange[0];
var Ref_Range = RefSheetRange[1];
if(!/return/i.test(SetSheetRange))
{
var SetSheetRange = SetSheetRange.split("!");
var Set_Sheet = SetSheetRange[0];
var Set_Range = SetSheetRange[1];
var RowVal = SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(Set_Range).getRow();
var ColVal = SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(Set_Range).getColumn();
}
var twoDimensionalArray = [];
var data = SpreadsheetApp.getActive().getSheetByName(Ref_Sheet).getRange(Ref_Range).getValues(); //Syncs sheet by name and range into var
for (var i = 0, Il=Search_Key.length; i<Il; i++) // i = number of rows to index and search
{
var Sending = []; //Making a Blank Array
var newArray = []; //Making a Blank Array
var Found ="";
for (var nn=0, NNL=data.length; nn<NNL; nn++) //nn = will be the number of row that the data is found at
{
if(Found==1 && ReturnMultiResults.toUpperCase() == 'N') //if statement for found if found = 1 it will to stop all other logic in nn loop from running
{
break; //Breaking nn loop once found
}
if (data[nn][SearchKey_Ref_IndexOffSet]==Search_Key[i]) //if statement is triggered when the search_key is found.
{
var newArray = [];
for (var cc=0, CCL=IndexOffSetForReturn.length; cc<CCL; cc++) //cc = numbers of columns to referance
{
var iosr = IndexOffSetForReturn[cc]; //Loading the value of current cc
var Sending = data[nn][iosr]; //Loading data of Level nn offset by value of cc
if(isEmpty_(Sending)) //if statement for if one of the returned Column level cells are blank
{
var Sending = "#N/A"; //Sets #N/A on all column levels that are blank
}
if (CCL>1) //if statement for multi-Column returns
{
newArray.push(Sending);
if(CCL-1 == cc) //if statement for pulling all columns into larger array
{
twoDimensionalArray.push(newArray);
var Found = 1; //Modifying found to 1 if found to stop all other logic in nn loop
break; //Breaking cc loop once found
}
}
else if (CCL<=1) //if statement for single-Column returns
{
twoDimensionalArray.push(Sending);
var Found = 1; //Modifying found to 1 if found to stop all other logic in nn loop
break; //Breaking cc loop once found
}
}
}
if(NNL-1==nn && isEmpty_(Sending)) //following if statement is for if the current item in lookup array is not found. Nessessary for data structure.
{
for(var na=0,NAL=IndexOffSetForReturn.length;na<NAL;na++) //looping for the number of columns to place "#N/A" in to preserve data structure
{
if (NAL<=1) //checks to see if it's a single column return
{
var Sending = "#N/A";
twoDimensionalArray.push(Sending);
}
else if (NAL>1) //checks to see if it's a Multi column return
{
var Sending = "#N/A";
newArray.push(Sending);
}
}
if (NAL>1) //checks to see if it's a Multi column return
{
twoDimensionalArray.push(newArray);
}
}
}
}
if (CCL<=1) //checks to see if it's a single column return for running setValue
{
var singleArrayForm = [];
for (var l = 0,lL=twoDimensionalArray.length; l<lL; l++) //Builds 2d Looping-Array to allow choosing of columns at a future point
{
singleArrayForm.push([twoDimensionalArray[l]]);
}
if(!/return/i.test(SetSheetRange))
{
SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(RowVal,ColVal,singleArrayForm.length,singleArrayForm[0].length).setValues(singleArrayForm);
SpreadsheetApp.flush();
if(/y/i.test(Add_Note))
{
SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(RowVal,ColVal,1,1).setNote("VLookup Script Ran On: " + Utilities.formatDate(new Date(), "PST", "MM-dd-yyyy hh:mm a") + "\nRange: " + Ref_Sheet + "!" + Ref_Range);
}
}
else
{
return singleArrayForm
}
}
if (CCL>1) //checks to see if it's a multi column return for running setValues
{
if(!/return/i.test(SetSheetRange))
{
SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(RowVal,ColVal,twoDimensionalArray.length,twoDimensionalArray[0].length).setValues(twoDimensionalArray);
SpreadsheetApp.flush();
if(/y/i.test(Add_Note))
{
SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(RowVal,ColVal,1,1).setNote("VLookup Script Ran On: " + Utilities.formatDate(new Date(), "PST", "MM-dd-yyyy hh:mm a") + "\nRange: " + Ref_Sheet + "!" + Ref_Range);
}
}
else
{
return twoDimensionalArray
}
}
}
//~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`
//~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`
// Empty String Check
function isEmpty_(string)
{
if(Object.prototype.toString.call(string) == '[object Boolean]') return false;
if(!string) return true;
if(string == '') return true;
if(string === false) return true;
if(string === null) return true;
if(string == undefined) return true;
string = string+' '; // check for a bunch of whitespace
if('' == (string.replace(/^\s\s*/, '').replace(/\s\s*$/, ''))) return true;
return false;
}
//~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`