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.

Read the guide →

Frequently Asked Questions

What is the difference between "difference" and "symmetric difference"?
The difference of A and B (A - B) is everything in A that is not in B - it ignores B-only items entirely. Symmetric difference is everything that is in exactly one of the two lists (A-only plus B-only combined), and is usually written A ^ B or A Δ B.
Which language is fastest for comparing two very large lists?
In every language listed here, the fast approach is the same: put one list into a hash-based structure (a set, HashSet, or dictionary) and check membership against it, giving roughly O(n + m) instead of the O(n * m) you get from nested loops or repeated linear search.
Do these approaches handle duplicates and preserve order?
Set-based approaches (Python sets, JavaScript Set, Java HashSet, SQL EXCEPT) collapse duplicates and do not preserve original order. If you need to keep duplicates and the original order, use a list comprehension or filter that checks membership against a set built from the other list, rather than converting your primary list into a set.
Is this the same problem as a "diff" between two text files?
No - a text diff (like the output of the Unix diff command) is about matching lines by position and showing insertions/deletions to turn one file into another. List difference is about set membership - which items exist in one list but not the other - regardless of position. See our Text Diff tool if you actually need a line-by-line diff.
What if I don't want to write any code at all?
Paste both lists into the Compare 2 Lists tool and it computes A-only, B-only, intersection, and union instantly in the browser, with options for case sensitivity, extra spaces, and leading zeros.
How do I handle case-insensitive comparison in these examples?
Normalize both lists to the same case before comparing - lowercase every item with .lower() in Python, .toLowerCase() in JavaScript, LOWER() in SQL, .toLowerCase() in Java, or tr for Bash - then run the same difference logic on the normalized copies.

Share this tool

Featured on ToolDirs
© 2026 ListDiff

Free online list and text processing tools.