4

In google app scripts I have a one dimensional data array that I can get values from like this:

data[0]

I'd love to be able to pass in the column name instead like this:

data("A")

That way I don't have to convert letters into their array position. So I'd like to extend the array object (extending isn't really risky since it's running in an isolated script environment).

I know I can add a function to the array prototype using this letter to number function and this object extension question like this:

Array.prototype.byCol = function(colName) {
  return this[getColumnNumber(colName) - 1];
}

function getColumnNumber(str) {
  var out = 0, len = str.length;
  for (pos = 0; pos < len; pos++) {
    out += (str.charCodeAt(pos) - 64) * Math.pow(26, len - pos - 1);
  }
  return out;
}

var data = [1,2,3,4];

document.write(data.byCol("B"));

But this is a slightly bulkier calling syntax than I wanted.

Based on this question on default functions, it looks like it's possible to assign a default function to an object, but they're doing so by just creating a function object like this:

var test = new func(function() {
    // do something
});

Can I get extend the array so that it will execute a default function when called as a method?

Community
  • 1
  • 1
KyleMit
  • 30,350
  • 66
  • 462
  • 664
  • 1
    You can't create something that is both an array and a function. Do you need it to be an array? It sounds like what you want is a regular object. – JLRishe Mar 13 '15 at 20:54
  • @JLRishe, it doesn't *need* to be an array, but that's what's most convenient as it's what's returned by [`getValues`](https://developers.google.com/apps-script/reference/spreadsheet/range#getValues()). I could probably wrap the whole thing in an object, but I think it's an interesting problem anyway. – KyleMit Mar 13 '15 at 20:57
  • @t.niese, it's being run on the Google Apps Script server which provides an execution environment for javascript. But it has a pretty narrow scope. I was only mentioning it to hedge against comments that advocated against extending objects I don't own. I'm not as concerned about it here. – KyleMit Mar 13 '15 at 20:58
  • If you don't need to support legacy browsers, you might be able to get away with using a `Map`. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map – Pete Mar 13 '15 at 21:04
  • 1
    Just as a note why making an callable array won't be possible: A function is defined to have a property `length` and this property is `writeable: no`. So if build something using `__proto__` or similar that has the functions of `Array` and is callable, you end up in an callable _array_ that will fail to change it's `length`. – t.niese Mar 13 '15 at 21:36

1 Answers1

3

Put simply, you can't make something into a function if it's not already a function, and you can't really extend arrays.

What you can do is create a wrapper function that wraps an array and provides the functionality you want, and also include the ability to get back the original array if you need it:

var wrapper = (function() {
  function getColumnNumber(str) {
    return Array.prototype.reduce.call(str.toUpperCase(), function (t, c) {
        return 26 * t + c.charCodeAt(0) - 64;
    }, 0) - 1;
  }

  return function(arr) {
    return function(col, val) {
      if (arguments.length === 0) {
        return arr;
      }
      if (arguments.length > 1) {
        arr[getColumnNumber(col)] = val;
      }
      return arr[getColumnNumber(col)];
    };
  };
})();

var w = wrapper([10, 20, 30, 40, 50]);

snippet.log(w('D')); // 40

w('D', 33);          // set value

snippet.log(w('D')); // 33

w()[3] = 42;         // access underlying array directly
w().push(60);

snippet.log(w('D')); // 42
snippet.log(w('F')); // 60
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
JLRishe
  • 99,490
  • 19
  • 131
  • 169