How to Compare Two Arrays in Google Apps Script
Apps Script runs on the same V8 JavaScript engine as Chrome, so the array comparison logic is plain JavaScript - the part that actually trips people up is getting data in and out of a Google Sheet in the right shape before and after comparing it.
Quick answer
const setB = new Set(b); const aOnly = a.filter(x => !setB.has(x)); is the fast approach, same as any JavaScript array comparison. The Apps Script-specific gotcha is that Range.getValues() always returns a 2D array - flatten it or map to a single column first, e.g. range.getValues().map(row => row[0]), before running the comparison.
The comparison itself: plain JavaScript
const a = ['apple', 'banana', 'cherry']; const b = ['banana', 'date']; const setB = new Set(b); const aOnly = a.filter(x => !setB.has(x)); Logger.log(aOnly); // [apple, cherry]
This is identical to the standard JavaScript approach - build a Set from one array, filter the other against it. See the JavaScript array difference guide for symmetric difference and other variations, all of which work the same way inside Apps Script.
The gotcha: getValues() returns a 2D array
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
const range = sheet.getRange('A2:A100');
const raw = range.getValues();
// raw looks like [['apple'], ['banana'], ['cherry'], ...] - NOT ['apple', 'banana', 'cherry']
const colA = raw.map(row => row[0]).filter(v => v !== '');
// now colA is a flat array, with blank trailing rows removed Even a single-column range comes back as an array of rows, each row an array of that row's column values. Comparing two raw getValues() results directly against each other with a Set produces nothing useful, since each "item" is itself a one-element array. Map to row[0] (or use .flat() for a single column range) before doing anything else with the data.
A complete function: compare two sheet columns
function compareColumns() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('Sheet1');
const lastRowA = sheet.getRange('A:A').getValues().filter(String).length;
const lastRowB = sheet.getRange('B:B').getValues().filter(String).length;
const colA = sheet.getRange(1, 1, lastRowA, 1).getValues().flat();
const colB = sheet.getRange(1, 2, lastRowB, 1).getValues().flat();
const setA = new Set(colA);
const setB = new Set(colB);
const aOnly = colA.filter(x => !setB.has(x));
const bOnly = colB.filter(x => !setA.has(x));
const resultsSheet = ss.getSheetByName('Results') || ss.insertSheet('Results');
resultsSheet.clear();
resultsSheet.getRange(1, 1, 1, 2).setValues([['A Only', 'B Only']]);
if (aOnly.length) {
resultsSheet.getRange(2, 1, aOnly.length, 1).setValues(aOnly.map(v => [v]));
}
if (bOnly.length) {
resultsSheet.getRange(2, 2, bOnly.length, 1).setValues(bOnly.map(v => [v]));
}
} This reads column A and column B from Sheet1, computes both A-only and B-only, and writes the results to a new tab called Results. Both reads and both writes are batched into single calls rather than looped cell-by-cell, which is the single biggest performance factor in any Apps Script that touches a range of any real size.
Comparing two Google Sheets columns without writing a script?
See the formula-based approach, plus a faster alternative.
Just need the answer, not the script? Paste both lists.
Open Compare 2 Lists →Frequently Asked Questions
What is the fastest way to compare two arrays in Google Apps Script?
const setB = new Set(b); const aOnly = a.filter(x => !setB.has(x));. This is O(n + m) instead of the O(n * m) you get from a.filter(x => !b.includes(x)) on large arrays.Why does comparing two Google Sheets columns in Apps Script give me weird nested array errors?
.flat() or map to the single column you need: const colA = range.getValues().map(row => row[0]);.Is indexOf or includes() ever fine to use in Apps Script instead of a Set?
How do I write the results back to a Google Sheet from Apps Script?
sheet.getRange(1, 1, results.length, 1).setValues(results.map(v => [v]));. setValues() requires the array dimensions to exactly match the target range's row and column count.Should I read and write cells one at a time in a loop instead?
How do I compare two columns case-insensitively in Apps Script?
const setB = new Set(colB.map(x => String(x).toLowerCase())); then filter colA the same way. Wrapping in String() first avoids an error if a cell contains a number rather than text.