Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google App Script. Check if Range1 intersects Range2

Good day everyone!

I'd like to have a function similiar to VBA's intersect in Google Sheets Script. Is there a nifty way to do this? The function must return:

  • RangeIntersect(R1, R2) = true if R1 has common cells with R2,
  • RangeIntersect(R1, R2) = false if R1 has no common cell with R2

Thank you in advance.

Speed test

I want the function to work as fast as possible. That's because it would be used in loops inside onEdit function. If you like, test provided function with this script:

function speedtest () {
  var sheet;      
  sheet = SpreadsheetApp.getActiveSheet();      
  var rr1 = ['A1:C16', 'B2:B88', 'D1:D8', 'E1:E17', 'A18:B51', 'A13:A14', 'A17:C17'];

  var r1, r2;

  r1 = sheet.getRange(rr1[0]);      
  var rr2 = [];

  // define some ranges
  for (var x = 0; x < 30; x++) {
  for (var i = 0; i < rr1.length; i++) {
    r2 = sheet.getRange(rr1[i]);
    rr2.push(r2);
  }
  }

  var C;

  var start, end, time;
  // timer start     

    for (var t = 0; t < 10; t++) {
        start = new Date().getTime();

        for (var f = 0; f < rr2.length; f++) {
             C = RangeIntersect(r1, rr2[f]);               
        }

        end = new Date().getTime();
        time = end - start;
        Logger.log('Execution time = ' + time);    

    }            
}
like image 689
Max Makhrov Avatar asked Apr 01 '16 14:04

Max Makhrov


People also ask

What is === in Google script?

Equality (==): a == b results in true if the value a is equal to value b. Strict equality (===): a === b results in true if the value a is equal to value b and their types are also the same. Inequality (! =): a !=

How do I find the range in Appscript?

Use getRangeByName() to reference a named range by using its name. Use getRange() to reference a range using its A1 notation or row and column indices.

How do I find the length of a string in a Google script?

To use the LEN function, simply enter the text string that you want to calculate the number of characters for, and then press the Enter key. The LEN function will return the number of characters in the text string.


2 Answers

First, you will need to get your range coordinates:

function Coordinates (range) {
  var self = this
  ;
  self.x1 = range.getColumn();
  self.y1 = range.getRow();
  self.x2 = range.getLastColumn();
  self.y2 = range.getLastRow();
}

Second, you will need to know if the column bounds and row bounds overlap:

function Overlaps (a, b, c, d) {
  return (a >= c && a <= d) || (b >= c && b <= d) || (c >= a && c <= b) || (d >= a && d <= b);
}

Your requested function to compare two ranges:

function RangeIntersect (R1, R2) {

  R1 = new Coordinates (R1);
  R2 = new Coordinates (R2);

  return (Overlaps(R1.x1, R1.x2, R2.x1, R2.x2) && Overlaps(R1.y1, R1.y2, R2.y1, R2.y2));
}

And here's a quick test:

function test () {
  var sheet, r1, r2, r3
  ;
  sheet = SpreadsheetApp.getActiveSheet();
  r1 = sheet.getRange(1, 1, 2, 2);
  r2 = sheet.getRange(2, 2, 2, 2);
  r3 = sheet.getRange(4, 4);

  Logger.log("%s %s overlap %s", r1.getA1Notation(), (RangeIntersect(r1, r2) ? "does" : "does not"), r2.getA1Notation());
  Logger.log("%s %s overlap %s", r2.getA1Notation(), (RangeIntersect(r2, r3) ? "does" : "does not"), r3.getA1Notation());
  Logger.log("%s %s overlap %s", r1.getA1Notation(), (RangeIntersect(r1, r3) ? "does" : "does not"), r3.getA1Notation());

  return;
}

Please note that this is just a quick solution. Also note that getActiveRange will include any rows and columns hidden through hide or filtering and would otherwise be included in the selected range.

Added 2016-05-04

A shortened version of Max's quicker solution with a single return (I prefer a single exit point):

function RangeIntersect (R1, R2) {
  return (R1.getLastRow() >= R2.getRow()) && (R2.getLastRow() >= R1.getRow()) && (R1.getLastColumn() >= R2.getColumn()) && (R2.getLastColumn() >= R1.getColumn());
}
like image 92
Reno Blair Avatar answered Oct 20 '22 07:10

Reno Blair


I've made my own version of this script:

function RangeIntersect(R1, R2) {

  var LR1 = R1.getLastRow();
  var Ro2 = R2.getRow();
  if (LR1 < Ro2) return false;
  
  
  var LR2 = R2.getLastRow();
  var Ro1 = R1.getRow();
  if (LR2 < Ro1) return false;
  
  var LC1 = R1.getLastColumn();
  var C2 = R2.getColumn();
  if (LC1 < C2) return false;
  
  var LC2 = R2.getLastColumn();
  var C1 = R1.getColumn();
  if (LC2 < C1) return false;

  return true;

}

This script works ~1.4 times faster because it returns each time when 2 ranges don't intersect.

Update Tested with v8 engine. Speed of all suggested solutions are almost the same:

  • Script. Range intersect speed test
like image 30
Max Makhrov Avatar answered Oct 20 '22 06:10

Max Makhrov