google script, match column with other and grab next value - javascript

I'm trying to make a google script to automate some calculations in a google spreadsheet.
I have several things in this sheet and what i need is, for all the date values i have on A column (between rows 3 and 20) i need to search on the column T (between rows 17 and 50) and when a date match i need to grab the value in the cell next to the data in column T.
Then with this sum i need to go to the B move one row down and substract that sum from the above cell.
example:
A
17/8/2017
18/8/2017
19/8/2017
B
100
empty
empty
T
empty
empty
18/8/2017
18/8/2017
18/8/2017
19/8/2017
U
empty
empty
5
5
2
1
After running the script B should be:
100
88
87
My code:
function burnDown(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = sheet.getDataRange().getValues();
var completed = sheet.getRange(20, 20, 40);
for(var i = 3; i<20;i++){
for(var j = 1; i<50; j++){
var sum = 0;
if(data[i][0] == completed[j][0])
{
sum = sum + completed[j][20];
Logger.log(completed[j][21] + "" + sum);
}
j++;
}
i++;
}
}
I'm stuck at this point where I have to make the match but i'm getting the error: TypeError: Cannot read property "0" from undefined. (line 9, file "BurnDown"
Thanks,

No values in completed. Perhaps you need a getValues() somewhere
function burnDown()
{
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = sheet.getDataRange().getValues();
//var completed = sheet.getRange(20, 20, 40);//this is a range
var completed = sheet.getRange(20,20,40).getValues();//this creates a 2d array of values
for(var i = 3;i<20;i++)
{
for(var j = 1; j<50; j++)
{
var sum = 0;
if(data[i][0]==completed[j][0])
{
sum=sum + completed[j][20];
Logger.log(completed[j][21] + "" + sum);
}
//j++;//not sure why these are here the loop increments
}
//i++;//same question as above
}
}

Related

Google Apps Scripts - Out of Memory Error

I am writing about a problem my org is having, as a result I have to be a bit vague (though I have permission to post this).
I am writing a script parse a google-sheet that has a column with numerical data in it. I need the script to return a set of ranges (strings in A1 notation for use elsewhere). The ranges have to correspond to real ranges in the script for which the numerical data sums to as close to 0.25 as possible but not more than .25, and return all ranges in the sheet for which this is true. E.g. if my sheet data was
A (COLUMN HEADER)
DataHeader (A1)
.1 (A2)
.01 (A3)
.04 (A4)
.1 (A5)
.03 (A6)
.02 (A7)
.1 (A8)
.05 (A9)
.04 (A10)
.07 (A11)
my script should return the array [A2:A4,A5:A10,A11], because A2:A4 sum to exactly .25, while A5:A10 sum to below (but close to) .25 but including A11 would get us above .25. A11 is included because I still need the data there.
In reality, my script will return larger ranges in A1 notation, but the problem I'm having is occuring in my attempt to properly construct a running sum and return the rows of interest.
My script is broken into 2 functions at the moment, because I was tackling the problems I was having in pieces.
The first script IDs the following info: The data to examine, as well as the number of rows from the top of the sheet that the start of the data
var data
and
var A1offset
respectively
The second function does a running sum of an array of my data and build an array of row number pairs representing the ranges at which the sum is just below .25. I have pasted the portion throwing an error below.
var result = [] //empty array to store row number pairs.
//data is a previously obtained array of my data of interest
//A1offset is the number of rows between the top of the sheet and the start of data
for (var i = 0;i<data.length;i++){
cursum += data[i][0]//add current value to cursum
if( cursum >= .25 && result.length<1){//if we are # the beginning add first rownum (A1 Notation) of data to result
i=i-1 //Go back one so that i++ runs, we don't miss this datapoint.
cursum = 0 //reset
result.push([(A1offset),(i + A1offset )])//
}
else if( cursum >= .25 && result.length>=1){
i=i-1//go back one
cursum = 0//reset
result.push([(result[result.length-1][1]+1),(i+A1offset)]) //Add to result a pair of values, the
//row after the end of the last range and the current row.
}
}
The issue:
Previously, I did not have the
i = i-1
statement but I realized I needed that or
i--
However after adding these two statements, upon running my code I get an 'Out of Memory error' error.
However, this does not happen if I loop over a smaller dataset. Experimentally, I have found that if I use
for (var i=0;i<.85*data.length;i++){
it runs fine.
data has 4978 elements in it.
Question: Is there an upper limit to the number of iterations a google script can run? I was under the impression that if there was, it was much higher than 4978 (or closer to 5050, since I'm using the i=i-1 statement)
Or, did I simply write poor code? Or both?
Thank you for any insights you can provide, I don't have a CS degree so frequently I run into issues where I don't know what I don't know.
Please let me know if I have not provided enough information.'
Edited to add a link to a test version of my sheet as well as the entire script below (overcommented to be certain):
function IDDATA() {
//ID the data to sum
var spsheet = SpreadsheetApp.getActive(); //Get spreadsheet
var tab = spsheet.getSheetByName("Test") //Get data sheet
var Drows = tab.getRange("A:A").getValues() //All values in Column D
var filledvals = Drows.filter(String).length //Length of filled in rows in Column D
//Subtract out the header row from the length of values
// Logger.log(datarows)
var offset = 0 //Initialize at 0. Offset is the distance (ZERO INDEXED <-THIS IS IMPORTANT!) between row 1 and the data
var ct1 = 0//initialize counter 1 at 0
while(Drows[ct1][0].indexOf("DATA")<0){ //Look at the ct1 Row of the 0th column of Column D
ct1++//Count until a the row with DATA in it. indexOf(String) returns -1 if the string is not found.
}
offset = ct1 //set offset to ct1.
var A1offset = ct1 + 2//Assume that all data has a header row, and create an offset variable that points to the first row of NUMERICAL data.
var datarows = filledvals-2 //Gets us the ROW NUMBER of the last filled in row in D:D, which should be the LAST number in the data.
return([datarows,offset,A1offset])
}
function RUNSUM(){
var firstcol = "A"
var lastcol = "A"
var spsheet = SpreadsheetApp.getActive(); //Get spreadsheet
var tab = spsheet.getSheetByName("Test") //Get sheet
var vararray = IDDATA()
var len = vararray[0] //Length of data
var offset = vararray[1] //Unused?
var A1offset = vararray[2]//The offset from 0, but to be used in A1 Notation
// Logger.log(typeof(A1offset))
var startrow = "A"+A1offset//The first row to get data from in the column that has system size data.
var endrow = "A"+(len+A1offset)//The last row to get data from that has system size data.
var cursum = 0 //Initialize # 0, the current sum of system size data.
var range = tab.getRange(startrow+":"+endrow) //Range to examine, in A1 notation Could probably use other forms of getRange()....
var data = range.getValues() //Values in range to exampine
var testmax = Math.floor(.85*data.length)
var result = [] //Positions, initialize as empty array
//var exceptions = [] //Rows that contain non-numbers
for (var i = 0;i<.8*data.length;i++){//.8* because of memory error
/* if(isNaN(data[i][0])){ //if data[i][0] is Not a Number
exceptions.push((i+A1offset).toFixed(0))//push the row number to exclude from eventual csv
continue //skip to next i
}*/
cursum += data[i][0]//add current value to cursum
if( cursum > .25 && result.length<1){//if we are # the beginning, we need to know, see results.push statement below
i=i-1
cursum = 0 //reset
result.push([(A1offset),(i + A1offset )])//
}
else if( cursum > .25 && result.length>=1){
i=i-1//go back one
cursum = 0//reset
result.push([(result[result.length-1][1]+1),(i+A1offset)])
}
}
var rangearray = []
var intarray = [] //declare an empty array to intermediate array vals todo: remove in favor of single loop
for(var k = 0; k < result.length; k++){
intarray.push([result[k][0].toFixed(0),result[k][1].toFixed(0)])//build array of arrays of result STRING vals w/out decimal pts.
}
for (var j = 0;j<result.length;j++){
rangearray.push(firstcol+intarray[j][0]+":"+lastcol+intarray[j][1])
}
Logger.log(rangearray)
return rangearray
}
In the Sheet you provided, the cell 4548 contains the number 0.2584.
Your algorithm, upon finding that number, will insert it into the result array and decrement i, creating an infinite loop that appends data into your array.
In order to fix your issue, in the RUNSUM() function right after the for loop declaration (for (var i = 0;i<data.length;i++)), you should handle this situation (or otherwise, make your data "valid" by removing that outlier value).
Furthermore, I have rewritten the algorithm in the hopes of making it more clear and stable (see that it now handles the last range, and ignores values over 0.25). I believe this may be interesting to you:
function myAlgorithm() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Test');
var values = sheet.getRange("A6:A").getValues();
var l = 0;
var results = []; // 1-indexed results
var currentSum = 0;
var offset = 5;
for (var i=0; i<values.length && typeof values[i][0] == 'number'; i++) {
if (values[i][0] >= 0.25) {
// Ignore the value larger or equal than 0.25
Logger.log('Found value larger or equal than 0.25 at row %s', i+1+offset);
if (l<i) {
results.push([l+1+offset, i+offset, currentSum]);
}
l = i+1;
currentSum = 0;
} else {
if (currentSum + values[i][0] > 0.25) {
results.push([l+1+offset, i+offset, currentSum]);
l = i;
currentSum = values[i][0];
} else {
currentSum += values[i][0];
}
}
}
// handle last range after exitig the for-loop
if (l<i) {
results.push([l+1+offset, i+offset, currentSum]);
}
for (var i=0; i<results.length; i++) {
Logger.log(results[i]);
}
}
Finally, if you decide to modify your code in order to handle this situation, I suggest you check out the information in the following link:
Troubleshooting in GAS

How to use variable inside for loop

I have 50 rows in google spreadsheet. User use these rows as input. But not all 50 rows are shown. When user input number in A2 cell then only that number of the rows are visible. I was able to do the task. But there is one problem inside for loop.
I think problem is in this line
for(var i = 2; i < fillingRows; i++){
//This is my full code.
function onEdit(e) {
//Logger.log("Previous value was - " + e.oldValue);
//Logger.log("New value is - " + e.value);
var range = e.range;
var rowNumber = range.getRow();
var columnNumber = range.getColumn();
//Logger.log("Row - " + rowNumber);
//Logger.log("Column - " + columnNumber);
if (rowNumber == 2 && columnNumber == 1){
//Logger.log(e.value);
var fillingRows = e.value + 2;
//Hide rows from 3 to 51
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('3:51').activate();
spreadsheet.getActiveSheet().hideRows(spreadsheet.getActiveRange().getRow(), spreadsheet.getActiveRange().getNumRows());
Logger.log(fillingRows);
for(var i = 2; i < fillingRows; i++){
spreadsheet.getActiveSheet().showRows(i, 1);
}
}
}
When I change that line to something like this
for(var i = 2; i < 10; i++){
Then it works correctly. But I can't use constant number here.
Isn't it possible to use variable inside a for loop.
Try this:
function onEdit(e) {
var sh=e.range.getSheet();
if(sh.getName()!="Your sheet name")return;//Edit this. Add your sheet name
if(e.range.rowStart==2 && e.range.columnStart==1 && e.value>=1 && e.value<=50){
//e.source.toast('Value: ' + e.value);
sh.hideRows(3,50);
sh.showRows(3,e.value);
//sh.getRange(1,1).setValue(e.value);
}
}
It's probably because fillingRows is no greater than 2, and therefore,
I suggest printing & checking fillingRows.

Google sheets script - Strip extra data from a second line and add it to the first line

I'm trying to build a script in google sheets script that will iterate down a sheet, row by row, and if it encounters an ID number in the first cell of that row that is identical to the one above it, it strips out the data from every cell EXCEPT Column A and B, and appends it to the line above. Ideally, this would work with an indeterminate number of duplicate row IDs, might be 2, might be 3, might be 4.
After stripping out the data I want to keep (eg colums C and onward), I then want to delete the entire contents of the processed duplicate ID row, but I just haven't put that in my script until after it copies the data correctly.
In this example, sheet rows 6, 7 and 8 have identical ID numbers (Column A)
Here is the result I'm trying to get:
And here is the result I'm getting:
I've tried a number of different ways, and torn down and rebuilt my script a couple of times without getting the result I want:
function stripMiner() {
var ss = SpreadsheetApp.openById("1WDPoTICQvdruxfhAwHLtA51fz05DqyZ-NhNfpAyPO6Y");
var mainSheet = ss.getSheetByName("Main");
var startRow = 5;
var numRows = mainSheet.getLastRow();//obtains the last row in the sheet
var setrgh = mainSheet
var dataRange = mainSheet.getRange(startRow, 1,4,120); //rowStart, columnStart, row count, column count, the columncount needs to be large enough to encompass all your ancillary data
var data = dataRange.getValues();
var iter = 0;
var maxItRow = 4;
var prevIdNum = 0;
var dupCount = 1;
var cc1 = "P5"; //Cells to dump check values into
var cc2 = "P6";
var dumpRow = startRow;
//if (numRows >= maxItRow){var maxIter = maxItRow;}
for (i in data){
if (iter != maxItRow){ //making sure we haven't gone over the iteration limit
var row = data[i];
var idNum = (row[0]);
var jCount = 0; //resets icount if the id number is different icount is used to skip some cells in a row
if (idNum == prevIdNum){//only proceed if we've hit another line with the same ID number
dupCount = +1; //increment the dupcount value
mainSheet.getRange(cc2).setValue("dupCount"+dupCount); //dupcount check value
var rowIterStart = 5; //RowIterStart is used to add to rowiter, EG if your data is 20 columns wide, and you start transposing from column 4, then this will want to be about 17
var rowIter = 1;
for (j in row){
if (jCount >= 2){ //the integer here is the column where it will begin to transpose data
mainSheet.getRange(dumpRow-1,(rowIterStart*dupCount)+(rowIter)).setValue(row[j]); //startRow+(iter-dupCount)
mainSheet.getRange(cc1).setValue("dumprow"+dumpRow);
}
rowIter+=1;
jCount +=1;
}
}
else{
var dupCount = 1;
dumpRow +=1;
}
prevIdNum = (row[0]); //sets the most recently processed rows ID number
}
iter +=1;
}
}
I'm not quite sure where I'm going wrong. Does anyone have any suggestions? Thanks!
(Also I'm still just a beginner with this so if I've overlooked anything obvious or taken the wrong approach to do this, I apologize!)
The results for the questioner's code in the case of copied data arise from a convoluted loop. In essence, though duplicates were identified, there was a mis-counting to assign the copied data to the correct rowID. So far as clearing data, no provision was included.
The following code works to meet the questioner's goals, though it is far from perfect.
At present, the recalculation of the "last column" after copy each duplicate is an absolute rather than than a row-based figure. So, if a duplicate was detected for, say, ID=3, the data would be copied to column 12 rather than column 6. This requires the addition of a simple dupID row counter.
The second factor is the calculation of the last column in the spreadsheet.
var dataRange = mainSheet.getRange(startRow, 1,Rowlast+1,120);
The questioner used 120 columns; and I have retained that number simply for the sake of consistency. The questioner should re-assess whether this is excessive.
function ejb_so_5284922701() {
var ss = SpreadsheetApp.openById("<< insert questioners spreadsheet ID>>");
var mainSheet = ss.getSheetByName("<< insert questioner's sheet name >>");
var startRow = 5;
// calculate the last row containing data
var Rowvals = ss.getRange("A5:A").getValues();
var Rowlast = Rowvals.filter(String).length; //6
Logger.log("last row = " + Rowlast); // DEBUG
// calculate the last column containing data
var cell = mainSheet.getRange("A5"); //or however you determine "cell"
var drCol = mainSheet.getDataRange().getLastColumn();
Logger.log('getLastColumn = ' + drCol); //DEBUG
for (var i = drCol; i >= 1; i--) {
if (mainSheet.getRange(cell.getRow(), i).getValue() != "") {
break;
}
}
var lastColumn = i;
Logger.log("Last column with data = " + lastColumn); //DEBUG
var setrgh = mainSheet
// numColumns neds to be reviewed
var dataRange = mainSheet.getRange(startRow, 1, Rowlast + 1, 120); //rowStart, columnStart, row count, column count, the column count needs to be large enough to encompass all your ancillary data
// start row = 5, 1= column A, 4, rows, 120, columns
Logger.log("startRow = " + startRow + ", and the datarange = " + dataRange.getA1Notation()); //DEBUG
var data = dataRange.getValues();
Logger.log("length of data =" + data.length); //DEBUG
var lastid = 0;
for (i = 0; i < data.length; i++) {
if (i == 0) {
// if this is the first row, then assign anything but zero to last id
lastid = 100;
Logger.log(" this is the first row; set last id to 100");
}
var thisid = data[i][0];
// evaluate whether this is a duplicate ID
if (thisid == lastid) {
// this is a dup
Logger.log("i=" + i + ". This is a dup" + ", name is " + data[i][2]); //DEBUG
var stufftocopyrange = mainSheet.getRange(startRow + i, 3, 1, 3);
var stufftocopy = stufftocopyrange.getValues();
Logger.log("the range to copy is " + stufftocopyrange.getA1Notation()); //DEBUG
var targetrange = mainSheet.getRange(startRow + lastid - 1, lastColumn + 1, 1, 3);
targetrange.setValues(stufftocopy);
lastColumn = lastColumn + 3;
var duprange = mainSheet.getRange(startRow + i, 1, 1, 5);
Logger.log("the range to clear is " + duprange.getA1Notation()); //DEBUG
duprange.clearContent();
} else {
// no dup
//assign lastid value
var lastid = thisid;
Logger.log("i=" + i + ". No dup. Last id set to " + lastid); // DEBUG
} // if
} // end for loop
}
BEFORE
AFTER
The solutions previously posted didn't quite get the result I needed, however I managed to cobble together something that works for my purposes. It expects to see data in the format like:
And turn it into something like this:
Where it uses duplicate ID numbers (with an indeterminite number of duplicates) to pull only certain columns of data from the duplicate lines and append it to the first line.
function stripMiner() {
var ss = SpreadsheetApp.openById("1WDPoTICQvdruxfhAwHLtA51fz05DqyZ-NhNfpAyPO6Y");
var mainSheet = ss.getSheetByName("Main");
var startRow = 5;
var numRows = mainSheet.getLastRow();//obtains the last row in the sheet
var setrgh = mainSheet
var dataRange = mainSheet.getRange(startRow, 1,3,120); //rowStart, columnStart, row count, column count, the columncount needs to be large enough to encompass all your ancillary data
var data = dataRange.getValues();
var iter = 0;
var maxItRow = 4;
var prevIdNum = 0;
var dupCount = 1;
var cc1 = "P5"; //Cells to dump check values into
var cc2 = "P6";
var dumpRow = startRow;
//if (numRows >= maxItRow){var maxIter = maxItRow;}
for (i in data){
if (iter != maxItRow){ //making sure we haven't gone over the iteration limit
var row = data[i];
var idNum = (row[0]);
var jCount = 0; //resets icount if the id number is different icount is used to skip some cells in a row
if (idNum == prevIdNum){//only proceed if we've hit another line with the same ID number
dupCount = +1; //increment the dupcount value
mainSheet.getRange(cc2).setValue("dupCount"+dupCount); //dupcount check value
var rowIterStart = 5; //RowIterStart is used to add to rowiter, EG if your data is 20 columns wide, and you start transposing from column 4, then this will want to be about 17
var rowIter = 1;
for (j in row){
if (jCount >= 2){ //the integer here is the column where it will begin to transpose data
mainSheet.getRange(dumpRow-2,(rowIterStart*dupCount)+(rowIter)).setValue(row[j]); //startRow+(iter-dupCount)
mainSheet.getRange(cc1).setValue("dumprow"+dumpRow);
}
rowIter+=1;
jCount +=1;
}
}
else{
var dupCount = 1;
dumpRow +=1;
}
prevIdNum = (row[0]); //sets the most recently processed rows ID number
}
iter +=1;
}
}
Hopefully someone else who wants to do a similar thing can make use of this too.

Service invoked too many times in a short time

I'm getting this error message to the following script. "Service invoked too many times in a short time: exec qps. Try Utilities.sleep(1000) between calls."
I've given my code below. Can you help me to stop this error message? Note: I'm using an array formula imported from another sheet. The trigger is set to work on Change.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Direct');
var range = sheet.getDataRange();
var values = range.getValues();
var rows_deleted = 0;
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
var value = values[i][j];
//row numbers are 1-based, not zero-based like this for-loop, so we add one AND...
//every time we delete a row, all of the rows move down one, so we will subtract this count
var row = i + 1 - rows_deleted;
//if the type is a number, we don't need to look
if (typeof value === 'string') {
var result = value.search("Remove");
//the .search() method returns the index of the substring, or -1 if it is not found
//we only care if it is found, so test for not -1
if (result !== -1) {
sheet.deleteRow(row);
rows_deleted++;
}
}
}
}
};

How can I remove rows with unique values, keeping rows with duplicate values?

I have a spreadsheet of surveys, in which I need to see how particular users have varied over time. As such, I need to disregard all rows with unique values in a particular column. The data looks like this:
Response Date Response_ID Account_ID Q.1
10/20/2011 12:03:43 PM 23655956 1168161 8
10/20/2011 03:52:57 PM 23660161 1168152 0
10/21/2011 10:55:54 AM 23672903 1166121 7
10/23/2011 04:28:16 PM 23694471 1144756 9
10/25/2011 06:30:52 AM 23732674 1167449 7
10/25/2011 07:52:28 AM 23734597 1087618 5
I've found a way to do so in Excel VBA:
Sub Del_Unique()
Application.ScreenUpdating = False
Columns("B:B").Insert Shift:=xlToRight
Columns("A:A").Copy Destination:=Columns("B:B")
i = Application.CountIf(Range("A:A"), "<>") + 50
If i > 65536 Then i = 65536
Do
If Application.CountIf(Range("B:B"), Range("A" & i)) = 1 Then
Rows(i).Delete
End If
i = i - 1
Loop Until i = 0
Columns("B:B").Delete
Application.ScreenUpdating = True
End Sub
I'd like to do it in Google Spreadsheets with a script that won't have to be changed. Closest I can get is retrieving all duplicate user ids from the range, but can't associate that with the row. That code follows:
function findDuplicatesInSelection() {
var activeRange = SpreadsheetApp.getActiveRange();
var values = activeRange.getValues();
// values that appear at least once
var once = {};
// values that appear at least twice
var twice = {};
// values that appear at least twice, stored in a pretty fashion!
var final = [];
for (var i = 0; i < values.length; i++) {
var inner = values[i];
for (var j = 0; j < inner.length; j++) {
var cell = inner[j];
if (cell == "") continue;
if (once.hasOwnProperty(cell)) {
if (!twice.hasOwnProperty(cell)) {
final.push(cell);
}
twice[cell] = 1;
} else {
once[cell] = 1;
}
}
}
if (final.length == 0) {
Browser.msgBox("No duplicates found");
} else {
Browser.msgBox("Duplicates are: " + final);
}
}
This is maybe not very efficient, but I think it's what you want:
var ar=[1,3,3,5,6,8,6,6];
console.log("Before:");
display(ar);//1 3 3 5 6 8 6 6
var index=[];
var ar2=[];
for(var a=0;a<ar.length;a++)
{
var duplicate=false;
for(var b=0;b<ar.length;b++)
{
if(ar[a]==ar[b]&&a!=b)
{
duplicate=true;
}
}
if(!duplicate)
{
index.push(a);
}
}
for(var a=0;a<index.length;a++)
{
ar[index[a]]=null;
}
for(var a=0;a<ar.length;a++)
{
if(ar[a]!=null)ar2.push(ar[a]);
}
console.log("After:");
display(ar2);//3 3 6 6 6
function display(x)
{
for(var a=0;a<x.length;a++)console.log(x[a]);
}
The fiddle : http://jsfiddle.net/mageek/6AGQ4/
And a shorter version that is as a function :
var ar=[1,3,3,5,6,8,6,6];
function removeUnique(x)
{
var index=[];
var ar2=[];
for(var a=0;a<ar.length;a++)
{
var duplicate=0;
for(var b=0;b<ar.length;b++)if(ar[a]==ar[b]&&a!=b)duplicate=1;
if(!duplicate)index.push(a);
}
for(var a=0;a<index.length;a++)ar[index[a]]=null;
for(var a=0;a<ar.length;a++)if(ar[a]!=null)ar2.push(ar[a]);
return x;
}
ar=removeUnique(ar);
The fiddle : http://jsfiddle.net/mageek/6AGQ4/2
I'd suggest going for something simple.
Create a short script that flags duplicates
Write the formula directly into the cell "=flagDuplicate(C2,C$2:C$10)"
Copy the forumla down the column
Use Spreadsheet's built in QUERY formula to pull the information you need
"=QUERY(A1:E10; "SELECT * WHERE E = TRUE"; 1)"
Here is a simple function to flag duplicates
function flagDuplicate(value, array) {
var duplicateCounter = 0;
for (var i=0; i<array.length; i++){
if (array[i] == value){ // I avoid === in Spreadsheet functions
duplicateCounter++;
}
}
if (duplicateCounter > 1){
return true;
}else{
return false;
}
}
Too many functions on a large table can slow things down. If it becomes a problem, you can always copy and "paste values only" - that will retain the information but remove the functions.
Best of luck.
Note: When I tested this I noticed that can take a while before the spreadsheet recognizes the new custom function (gives error like can't find function FLAGDUPLICATE)
You could also do it using arrays to handle the whole sheet at once :
function removeUnique(){
var col = 2 ; // choose the column you want to check for unique elements
var sh = SpreadsheetApp.getActiveSheet();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data=ss.getDataRange().getValues();// get all data
data.sort(function(x,y){
// var xp = Number(x[col]);// use these to sort on numeric values
// var yp = Number(y[col]);
var xp = x[col];// use these for non-numeric values
var yp = y[col];
Logger.log(xp+' '+yp); // just to check the sort is OK
return xp == yp ? 0 : xp < yp ? -1 : 1;// sort on column col numeric ascending
});
var cc=0;
var newdata = new Array();
for(nn=0;nn<data.length-1;++nn){
if(data[nn+1][col]==data[nn][col]||cc>0){
newdata.push(data[nn]);
++cc;
if(cc>1){cc=0}}
}
ss.getDataRange().clearContent(); // clear the sheet
sh.getRange(1,1,newdata.length,newdata[0].length).setValues(newdata);// paste new values sorted and without unique elements
}
EDIT : here is the version that keeps all duplicates (the working one)
function removeUnique(){
var col = 2 ; // choose the column you want to check for unique elements
var sh = SpreadsheetApp.getActiveSheet();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data=ss.getDataRange().getValues();// get all data
data.sort(function(x,y){
// var xp = Number(x[col]);// use these to sort on numeric values
// var yp = Number(y[col]);
var xp = x[col];// use these for non-numeric values
var yp = y[col];
Logger.log(xp+' '+yp); // just to check the sort is OK
return xp == yp ? 0 : xp < yp ? -1 : 1;// sort on column col numeric ascending
});
var newdata = new Array();
for(nn=0;nn<data.length-1;++nn){
if(data[nn+1][col]==data[nn][col]){
newdata.push(data[nn]);
}
}
if(data[nn-1][col]==data[nn][col]){newdata.push(data[nn])}
ss.getDataRange().clearContent(); // clear the sheet
sh.getRange(1,1,newdata.length,newdata[0].length).setValues(newdata);// paste new values sorted and without unique elements
}

Categories

Resources