4

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 |   |   |
+---+---+---+---+
chuckx
  • 6,484
  • 1
  • 22
  • 23
Buntel
  • 540
  • 6
  • 19
  • Are you asking about implementing a [custom function via Apps Script](https://developers.google.com/apps-script/guides/sheets/functions)? – chuckx Jun 23 '18 at 04:11
  • Yes I am. Do you know wether it's possible or not? – Buntel Jun 23 '18 at 08:43
  • As far as I know, getting a [Range object](https://developers.google.com/apps-script/reference/spreadsheet/range) representing the single cell is not possible. The documentation clearly lays out [the rules for argument handling in custom functions](https://developers.google.com/apps-script/guides/sheets/functions#arguments). If you describe what you're trying to accomplish, that may allow for alternative approaches to be suggested. – chuckx Jun 23 '18 at 08:48
  • First of all thanks very much for the help! I'll try to explain what I accomplish: Okay We have A1 = 1, A2 =, A3 = 3, A4 = 4, B1 = A1. If I copy this to B1:C2 I want that the reference takes new line whether moving to the right or down. Result should be. B1 = 1, C1 = 2, B2 = 3, C2 = 4. Thanks in advance. – Buntel Jun 23 '18 at 09:19
  • 2
    See [passing cell references to spreadsheet functions](https://stackoverflow.com/q/12206399) –  Jun 23 '18 at 11:31
  • Instead of putting question details in comments, it's better to update your question. That allows for editing and correcting. For example, I'm guessing you meant "A2 = 2"? – chuckx Jun 23 '18 at 18:49
  • I took some liberty transcribing the comment into the question. Let me know if this captures what you're looking for. – chuckx Jun 23 '18 at 21:30
  • 1
    Possible duplicate of [passing cell references to spreadsheet functions](https://stackoverflow.com/questions/12206399/passing-cell-references-to-spreadsheet-functions) – Rubén Jun 24 '18 at 18:08

1 Answers1

2

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]]
chuckx
  • 6,484
  • 1
  • 22
  • 23
  • here you can see what for me worked well https://pastebin.com/MQfxQGW1. In this case it's possible to do further calculation with the mapped values if needed. Thanks for your help ;) – Buntel Jun 24 '18 at 10:00
  • A couple points: **1.** It looks like you're calculating a single value for each cell which requires executing this script for every cell in the destination range. Returning a 2D array from a single formula execution is both simpler to manage (i.e. a single `=formula()` instance in the sheet) and much more efficient. **2.** [In JSDoc, the syntax is for a parameter is `@param {} `.](http://usejsdoc.org/tags-param.html) In all of your `@param` entries you've named them `input` instead of using the matching variable names from the function declaration. – chuckx Jun 24 '18 at 15:19
  • Thanks for your advices . I didn't know about JsDocs . I've splitted the function. One is going to return a matrix an the other is going to return a single value. I want to this because of later manipulation (e.g. =_map(some parameters)*2). I think this is not able if the function returns a matrix. – Buntel Jun 24 '18 at 16:05
  • FYI, added an example of performing arbitrary transformations. May not fit your use case but thought it might be good food for thought. – chuckx Jun 25 '18 at 17:55