How to Find the Difference Between Two Lists in Every Major Language
"Difference between two lists" means the same thing no matter what language you're writing in: which items are only in list A, which are only in list B, and which are in both. Every language below solves it a slightly different way, but the underlying operations are identical. This page is a quick reference - jump to your language, or use the linked full guide for more approaches and pitfalls.
Quick answer
There are three operations people mean by 'list difference': A-only (items in A but not B), symmetric difference (items in exactly one of the two lists), and intersection (items in both). Every language has a fast way to compute these using a hash-based set, and a slower fallback using nested loops or linear search - the code below shows the fast version for each.
The three operations, defined once
A-only (difference): items in A that don't appear in B. Symmetric difference: items in exactly one list - A-only plus B-only. Intersection: items that appear in both. Every snippet below computes A-only; most languages make symmetric difference and intersection a one-line variation, noted inline.
Python
a = ['apple', 'banana', 'cherry'] b = ['banana', 'date'] a_only = list(set(a) - set(b)) # difference sym_diff = list(set(a) ^ set(b)) # symmetric difference both = list(set(a) & set(b)) # intersection print(a_only) # ['apple', 'cherry']
Sets drop duplicates and don't preserve order. See the full guide for an order-preserving approach and collections.Counter for multiset difference. Full Python guide →
Pandas (Python DataFrames)
only_in_df1 = df1.merge(df2, on='id', how='outer', indicator=True) \
.query('_merge == "left_only"') For plain Python lists, set(a) - set(b) above is all you need. For pandas DataFrames, merge(..., indicator=True) is the equivalent - it adds a column showing which rows came from which frame after an outer join. Full pandas guide →
JavaScript
const a = ['apple', 'banana', 'cherry']; const b = ['banana', 'date']; const setB = new Set(b); const aOnly = a.filter(x => !setB.has(x)); console.log(aOnly); // ['apple', 'cherry']
Filtering with .includes() directly on an array works but is O(n * m) for large lists - building a Set first keeps it linear. Full JavaScript guide →
TypeScript
function difference<T>(a: readonly T[], b: readonly T[]): T[] {
const setB = new Set(b);
return a.filter(x => !setB.has(x));
}
const aOnly = difference(['apple', 'banana', 'cherry'], ['banana', 'date']);
console.log(aOnly); // ['apple', 'cherry'] Same runtime logic as JavaScript, wrapped in a generic function so T is inferred and type-checked at every call site instead of copy-pasted per array type. Full TypeScript guide →
SQL
SELECT id FROM table_a EXCEPT SELECT id FROM table_b;
Oracle uses MINUS instead of EXCEPT. NOT IN and NOT EXISTS are common alternatives with different NULL-handling behavior. Full SQL guide →
Java
List<String> a = new ArrayList<>(List.of("apple", "banana", "cherry"));
List<String> b = List.of("banana", "date");
Set<String> setB = new HashSet<>(b);
List<String> aOnly = a.stream()
.filter(x -> !setB.contains(x))
.collect(Collectors.toList()); a.removeAll(b) mutates a and is O(n * m) unless b is already a HashSet. Full Java guide →
Kotlin
val a = listOf("apple", "banana", "cherry")
val b = listOf("banana", "date")
val aOnly = a - b
println(aOnly) // [apple, cherry] The - operator handles small lists cleanly; convert b to a Set first for large lists to guarantee O(n + m) instead of a possible O(n * m). Full Kotlin guide →
Swift
let a = ["apple", "banana", "cherry"] let b = ["banana", "date"] let aOnly = Array(Set(a).subtracting(b)) print(aOnly) // ["apple", "cherry"]
Set(a).subtracting(b) is O(n + m); Swift also has CollectionDifference (a.difference(from: b)) for a positional diff with insert/remove index information, not just membership. Full Swift guide →
C#
var aOnly = a.Except(b).ToList();
LINQ's Except() runs in roughly O(n + m) using a hash lookup internally, but like every set-based approach it drops duplicates from a and doesn't preserve order. Full C# guide →
PHP
$aOnly = array_diff($a, $b);
array_diff() compares values as strings and keeps $a's original array keys. Full PHP guide →
Go
setB := make(map[string]struct{})
for _, v := range b {
setB[v] = struct{}{}
}
for _, v := range a {
if _, found := setB[v]; !found {
aOnly = append(aOnly, v)
}
} Go has no built-in set type, so the idiomatic approach is a map[T]struct{} used purely for its O(1) key lookup. Full Go guide →
Ruby
a_only = a - b
Array#- removes every occurrence of any element of a that appears anywhere in b, while preserving order and any duplicates not found in b. Full Ruby guide →
R
a_only <- setdiff(a, b)
setdiff(a, b) is direction-sensitive and drops duplicates; use a[!a %in% b] to preserve them. Full R guide →
Bash
comm -23 <(sort a.txt) <(sort b.txt)
comm requires both files sorted first. For lines that shouldn't be reordered, grep -Fxvf b.txt a.txt does the same job without sorting. Full Bash guide →
PowerShell
Compare-Object $a $b | Where-Object SideIndicator -eq '<='
SideIndicator of <= means the item exists only in the first (reference) list. Compare-Object is case-insensitive by default. Full PowerShell guide →
VBA (Excel Macro)
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
For Each v In arrB
If Not dict.Exists(v) Then dict.Add v, True
Next v VBA has no set type either, so a Scripting.Dictionary used as a lookup table is the fast approach for comparing two Excel columns from a macro. Full VBA guide →
Excel
Excel doesn't have a built-in set-difference function, but COUNTIF, MATCH, and conditional formatting can all get you there with a formula per row. Full Excel guide →
Power Query
A Left Anti merge (Home > Merge Queries, Join Kind Left Anti) returns rows only in the first table - the same result as List.Difference in M code for plain lists. Full Power Query guide →
Google Sheets
Google Sheets has the same formula options as Excel, plus a couple of array-formula tricks Excel doesn't offer as cleanly. Full Google Sheets guide →
Google Apps Script
const setB = new Set(colB); const aOnly = colA.filter(x => !setB.has(x));
Same JavaScript logic as above, with one Apps Script-specific gotcha: Range.getValues() always returns a 2D array, so flatten or map to a single column before comparing. Full Google Apps Script guide →
Just need the answer, not the code? Paste both lists.
Open Compare 2 Lists →Want the concepts explained without code?
See the plain-language guide to comparing two lists.