Buntel
Buntel

Reputation: 622

How to pass a cell reference to an Apps Script custom function?

Assuming that:

A1 = 3
B1 = customFunc(A1)  // will be 3

In my custom function:

function customFunc(v) {
  return v;
}

v will be 3. But I want access the cell object A1.


The following is transcribed from the comment below.

Input:

+---+---+
|   | A |
+---+---+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
+---+---+

I want to copy A1:A4 to B1:C2 using a custom function.

Desired result:

+---+---+---+---+
|   | A | B | C |
+---+---+---+---+
| 1 | 1 | 1 | 2 |
| 2 | 2 | 3 | 4 |
| 3 | 3 |   |   |
| 4 | 4 |   |   |
+---+---+---+---+

Upvotes: 4

Views: 4215

Answers (1)

chuckx
chuckx

Reputation: 6877

To achieve the desired result of splitting an input list into multiple rows, you can try the following approach.

function customFunc(value) {
  if (!Array.isArray(value)) {
    return value;
  }
  // Filter input that is more than a single column or single row.
  if (value.length > 1 && value[0].length > 1) {
    throw "Must provide a single value, column or row as input";
  }
  var result;
  if (value.length == 1) {
    // Extract single row from 2D array.
    result = value[0];
  } else {
    // Extract single column from 2D array.
    result = value.map(function (x) {
      return x[0];
    });
  }
  // Return the extracted list split in half between two rows.
  return [
    result.slice(0, Math.round(result.length/2)),
    result.slice(Math.round(result.length/2))
  ];
}

Note that it doesn't require working with cell references. It purely deals with manipulating the input 2D array and returning a transformed 2D array.

Using the function produces the following results:

  • A1:A4 is hardcoded, B1 contains =customFunc(A1:A4)

    +---+---+---+---+
    |   | A | B | C |
    +---+---+---+---+
    | 1 | a | a | b |
    | 2 | b | c | d |
    | 3 | c |   |   |
    | 4 | d |   |   |
    +---+---+---+---+
    
  • A1:D4 is hardcoded, A2 contains =customFunc(A1:D4)

    +---+---+---+---+---+
    |   | A | B | C | D |
    +---+---+---+---+---+
    | 1 | a | b | c | d |
    | 2 | a | b |   |   |
    | 3 | c | d |   |   |
    +---+---+---+---+---+
    
  • A1:B2 is hardcoded, A3 contains =customFunc(A1:B2), the error message is "Must provide a single value, column or row as input"

    +---+---+---+---------+
    |   | A | B |    C    |
    +---+---+---+---------+
    | 1 | a | c | #ERROR! |
    | 2 | b | d |         |
    +---+---+---+---------+
    

This approach can be built upon to perform more complicated transformations by processing more arguments (i.e. number of rows to split into, number of items per row, split into rows instead of columns, etc.) or perhaps analyzing the values themselves.


A quick example of performing arbitrary transformations by creating a function that takes a function as an argument.

This approach has the following limitations though:

  • you can't specify a function in a cell formula, so you'd need to create wrapper functions to call from cell formulas
  • this performs a uniform transformation across all of the cell values

The function:

/**
 * @param {Object|Object[][]} value The cell value(s).
 * @param {function=} opt_transform An optional function to used to transform the values.
 * @returns {Object|Object[][]} The transformed values.
 */
function customFunc(value, opt_transform) {
  transform = opt_transform || function(x) { return x; };
  if (!Array.isArray(value)) {
    return transform(value);
  }
  // Filter input that is more than a single column or single row.
  if (value.length > 1 && value[0].length > 1) {
    throw "Must provide a single value, column or row as input";
  }
  var result;
  if (value.length == 1) {
    // Extract single row from 2D array.
    result = value[0].map(transform);
  } else {
    // Extract single column from 2D array.
    result = value.map(function (x) {
      return transform(x[0]);
    });
  }
  // Return the extracted list split in half between two rows.
  return [
    result.slice(0, Math.round(result.length/2)),
    result.slice(Math.round(result.length/2))
  ];
}

And a quick test:

function test_customFunc() {
  // Single cell.
  Logger.log(customFunc(2, function(x) { return x * 2; }));

  // Row of values.
  Logger.log(customFunc([[1, 2, 3 ,4]], function(x) { return x * 2; }));

  // Column of values.
  Logger.log(customFunc([[1], [2], [3], [4]], function(x) { return x * 2; }));
}

Which logs the following output:

[18-06-25 10:46:50:160 PDT] 4.0
[18-06-25 10:46:50:161 PDT] [[2.0, 4.0], [6.0, 8.0]]
[18-06-25 10:46:50:161 PDT] [[2.0, 4.0], [6.0, 8.0]]

Upvotes: 2

Related Questions