2

I'm fairly new to both Google Apps Script and JavaScript, but have done some extensive reading on similarities and differences between the two. However, despite this, I can't seem to understand why this custom function for Google Sheets that I've been working on will run correctly in Javascript, but not in GaS.

My basic goal is to check over a list of names, count any duplicates, and display the five names that show up the most, along with the count of how often they appeared. The code below only accounts for the top result.

The script so far is as follows:

//Initial Declarations
function watchList(data) {
  var first = {};
  var rcount = 0;
  if (data === undefined){data = SpreadsheetApp.getActiveSheet().getRange("B2:B139")}; //Utilized for testing within the script editor

  for (var i = 0; i < data.length; i++){//loop through the names

    rcount = 0;// Reset duplicate count

    for (var r = 0; r < data.length; r++) {//loop through the entire list again for each increment of the array above

      if (data[i] === data[r]) {//Is this name a duplicate?

        rcount++;//If yes, add 1 to the count

      } 
  } 
    if (first.count == undefined || first.count < rcount) {//Does this value have the most duplicates?

      first.name = data[i];//If so, assign the name and count values to the first object
      first.count = rcount;

  } 
  }
return [first.name, first.count];
}

What I return, though, is: first name on the list, 1

When I then punched all the code into a javascript editor, and logged the results to the console, it worked perfectly! The slightly altered (to account for there being no attached sheet) javascript code is below:

function watchList() {
  var range = new Array('Name 1', 'Name 1', 
               'Name 1','Name 2',
               'Name 2', 'Name 2', 
               'Name 2', 'Name 3');
  var first = {};
  var rcount = 0;

  for (var i = 0; i < range.length; i++){
    rcount = 0;
    for (var r = 0; r < range.length; r++) {

      if (range[i] == range[r]) {
        rcount++;   
      }   
  } 
    if (first.count === undefined || first.count < rcount) {

      first.name = range[i];
      first.count = rcount;

  } 
  }
 console.log([first.name, first.count]);
 return first.name;
}

watchList();

Would anyone be able to enlighten me as to whatever missteps I'm missing here? Thanks a ton in advance!

Edit - This has been linked as a duplicate of the "How to Compare JS Arrays" question, but seems, at least to me, to be a different issue, as that is regarding two entire arrays, while my question is looking to compare individual array elements against one another, and at a separate pace.

TheMaster
  • 45,448
  • 6
  • 62
  • 85
Crunchpuff
  • 23
  • 4
  • `getRange()` doesn't return a array – TheMaster Sep 30 '19 at 20:52
  • Possible duplicate of [How to compare arrays in JavaScript?](https://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript) – TheMaster Sep 30 '19 at 21:00
  • Welcome. What @TheMaster probably meant to mention is that you need to use `getValues()` to get data from the range. So it might look something like this: `data = SpreadsheetApp.getActiveSheet().getRange("B2:B139").getvalues();`. [Doc Ref](https://developers.google.com/apps-script/reference/spreadsheet/range#getvalues) – Tedinoz Sep 30 '19 at 21:24
  • @Tedinoz , thank you! And gotcha, that's good to know, but ultimately, the values are being pulled as an array when it's connected to the sheet via the initial line ("watchList(data)"); I've tried testing this by setting the return line to simply "return data;" which, if then used on the sheet, spits the entire array back out. – Crunchpuff Sep 30 '19 at 21:30
  • @TheMaster , I think that is a bit of a different issue, as that is comparing entire arrays, and this is comparing elements of the array against itself – Crunchpuff Sep 30 '19 at 21:33
  • *as that is comparing entire arrays,* which is exactly what you're doing. – TheMaster Sep 30 '19 at 21:41

1 Answers1

0

Issue(s):

  • getRange() doesn't return a array. .getValues() is needed to get array values from the range.

  • Assuming the above is a simple typo, getValues() doesn't return a return a 1 dimensional array either. It returns a 2D array. Custom functions also return a 2D array.

  • Illustrating the problem,

function watchList() {
  var range = new Array('Name 1', 'Name 1', 
           'Name 1','Name 2',
           'Name 2', 'Name 2', 
           'Name 2', 'Name 3').map(e=>[e]);// modified to mimick the 2D array returned by `getValues` or in a custom function
  var first = {};
  var rcount = 0;
  
  for (var i = 0; i < range.length; i++){
rcount = 0;
for (var r = 0; r < range.length; r++) {

  if (range[i] == range[r]) {//Two objects are never equal, except when they refer to the same object,i.e., the first time, when both sides refer to the same object in memory. See duplicate question.
    rcount++;   //rcount is 1
  }   
  } 
if (first.count === undefined || first.count < rcount) {//1<1 will never be true; The first name and  first count will stay
  
  first.name = range[i];
  first.count = rcount;
  
  } 
  }
 console.log([first.name, first.count]);
 return first.name;
}

watchList();

Solution:

  • Use .getValues() on the range
  • Compare primitives and not array objects.

Snippet:

function watchList() {
var range = [
  [ 'Name 1' ],
  [ 'Name 1' ],
  [ 'Name 1' ],
  [ 'Name 2' ],
  [ 'Name 2' ],
  [ 'Name 2' ],
  [ 'Name 2' ],
  [ 'Name 3' ]
];// simulate getValues() or custom function arguments B1:B8

  var first = {};
  var rcount = 0;
  
  for (var i = 0; i < range.length; i++){
rcount = 0;
for (var r = 0; r < range.length; r++) {

  if (range[i][0] == range[r][0]) {//compare primitives 
    rcount++;   
  }   
  } 
if (first.count === undefined || first.count < rcount) {
  
  first.name = range[i];
  first.count = rcount;
  
  } 
  }
 console.log([first.name, first.count]);
 return first.name;
}

watchList();

References:

Community
  • 1
  • 1
TheMaster
  • 45,448
  • 6
  • 62
  • 85
  • `getRange()` wasn't actually being used in my code, though. The range was being pulled via spreadsheet input--that was just in there so it wouldn't throw errors when running it within the script editor. That said, though, I didn't know that about `getValue()` so thank you – Crunchpuff Sep 30 '19 at 21:42
  • @Crunchpuff If that solved your problem, consider accepting the answer. Wherever you're pulling the range, you're pulling a 2D array and the issue is the same as illustrated. Custom functions also send a 2D array and not a 1D array as you've made it out in your JavaScript code. – TheMaster Sep 30 '19 at 21:46
  • got it--didn't mean to be unaccepting of it, just wanted to be sure I was clear on the logic. That did fix the issue, and I'll mark it as such. Thank you! – Crunchpuff Sep 30 '19 at 21:51
  • @Crunchpuff Edited my answer to reflect custom functions. – TheMaster Sep 30 '19 at 21:53