5
votes

I am trying to write a Google Sheets Apps Script function that checks the content of the current active cell, matches it to the content of another cell, then moves the cursor according to the result of that check.

For a spreadsheet as this example one: https://docs.google.com/spreadsheets/d/1kpuVT1ZkK0iOSy_nGNPxvXPTFJrX-0JgNmEev6U--5c/edit#gid=0

I would like the user to go to D2, enter a value followed by Tab, then while the active cell is in E2, the function will check if the value in D2 is the same in B2. If it is, stays in E2. Then we enter the value in E2 followed by Tab, the function checks if it's the same as C2, if it is, then moves from F2 down and left twice to D3. So if all the values are entered correctly, the cursor zig-zags between the cells in D, E and F as shown below:

enter image description here

The closest I could find is the answer to the one below, but it involves clicking on a method in the menu each time:

Move sheet rows on based on their value in a given column

I imagine the function could be triggered at the beginning of editing the document, then it keeps moving the cursor until the document is completed, at which point the function can be stopped.

Any ideas?

EDIT: what I've tried so far:

I have managed to change the position to a hard-coded position 'D3' and to create a function that moves one down with these functions:

function onOpen() {
  var m = SpreadsheetApp.getUi().createMenu('Move');
  m.addItem('Move to D3', 'move').addToUi();
  m.addItem('Move to one below', 'move2').addToUi();
  m.addItem('Move down left', 'move_down_left').addToUi();
}

function move() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var s = ss.getActiveSheet();
  var range = s.getRange('D3');
  s.setActiveRange(range);  
}

function move2() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var s = ss.getActiveSheet();
  var r = s.getActiveRange();
  var c = r.getCell(1,1);
  var target = s.getRange(c.getRow() + 1, c.getColumn());
  s.setActiveRange(target);
}

function move_down_left() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var s = ss.getActiveSheet();
  var r = s.getActiveRange();
  var c0 = r.getCell(1,1);
  var r1 = s.getRange(c0.getRow(), c0.getColumn() - 1);
  var c1 = r1.getCell(1,1);
  var r2 = s.getRange(c1.getRow(), c1.getColumn() - 2);
  var c2 = r2.getCell(1,1);
  if (c1.getValue() == c2.getValue()) {
    var target = s.getRange(c1.getRow() + 1, c1.getColumn() - 1);
    s.setActiveRange(target);
  }
}
1
Sure, you can do that with onEdit. You could use a menu-triggered function to "initiate" the flow. Consult the Spreadsheet Service documentation (particularly the Range and Sheet classes) for the relevant methods, such as value accessors, etc. you probably want to handle both the "entered successfully" and "not entered successfully" cases - often the natural behavior is to move down or right, depending on the user keypress. - tehhowch
Are you basically asking for someone to do this for you? What do you envision the answer being? A turnkey solution? You don't show any effort in solving this yourself. - tehhowch
Would it help if I try to write the functions and copy+paste what I have even if it's not working? - 719016
@71 Yes,It would. - TheMaster

1 Answers

2
votes

As I mentioned in my comment, you want to use a simple trigger function (so that it works for all users without requiring them to first authorize the script). There are naturally some limitations of simple triggers, but for the workflow you describe, they do not apply.

A key principle of a function receiving the on edit trigger invocation is that it has an event object with data about the cell(s) that were edited:

  • authMode: A value from the ScriptApp.AuthMode enum.
  • oldValue: Cell value prior to the edit, if any. Only available if the edited range is a single cell. Will be undefined if the cell had no previous content.
  • range: A Range object, representing the cell or range of cells that were edited.
  • source: A Spreadsheet object, representing the Google Sheets file to which the script is bound.
  • triggerUid: ID of trigger that produced this event (installable triggers only).
  • user: A User object, representing the active user, if available (depending on a complex set of security restrictions).
  • value: New cell value after the edit. Only available if the edited range is a single cell.

Of these, we will use range and value. I will leave the business case of handling edits to multiple-cell ranges to you. Stack Overflow is, after all, not where you obtain turnkey solutions ;)

function onEdit(e) {
  if (!e) throw new Error("You ran this from the script editor");
  const edited = e.range;
  if (edited.getNumRows() > 1 || edited.getNumColumns() > 1)
    return; // multicell edit logic not included.

  const sheet = edited.getSheet();
  if (sheet.getName() !== "your workflow sheet name")
    return;

  // If the user edited a specific column, check if the value matches that
  // in a different, specific column.
  const col = edited.getColumn(),
      advanceRightColumn = 5,
      rightwardsCheckColumn = 2;
  if (col === advanceRightColumn) {
    var checkedValue = edited.offset(0, rightwardsCheckColumn - col, 1, 1).getValue();
    if (checkedValue == e.value) // Strict equality may fail for numbers due to float vs int
      edited.offset(0, 1, 1, 1).activate();
    else
      edited.activate();
    return;
  }

  const endOfEntryColumn = 8,
      endCheckColumn = 3,
      startOfEntryColumn = 4;
  if (col === endOfEntryColumn) {
    var checkedValue = edited.offset(0, endCheckColumn - col, 1, 1).getValue();
    if (checkedValue == e.value)
      edited.offset(1, startOfEntryColumn - col, 1, 1).activate();
    else
      edited.activate();
    return;
  }
}

As you digest the above, you'll note that you are required to supply certain values that are particular to your own workflow, such as a sheet name, and the proper columns. The above can be modified in a fairly straightforward manner to advance rightward if the edited column is one of several columns, using either a constant offset to the respective "check" column, or an array of respectively-ordered offsets / target columns. (Such a modification would almost certainly require the use of Array#indexOf.)

A caveat I note is that strict equality === fails if your edits are numbers representable as integers, because Google Sheets will store the number as a float. Strict equality precludes type conversion by definition, and no int can ever be the exact same as a float. Thus, the generic equality == is used. The above code will not equate a blank check cell and the result of deleting content.

Method references: