How to Compare Two Lists in PowerShell

PowerShell has a cmdlet built specifically for this job - Compare-Object - along with a couple of operator-based shortcuts that read more naturally in a pipeline. The main things to get right: which SideIndicator means what, and the default case-insensitivity that catches almost everyone at least once.

Quick answer

Compare-Object $a $b | Where-Object SideIndicator -eq '<=' returns items only in $a. SideIndicator '<=' means the item exists only in the first (reference) list, '=>' means only in the second, and '==' means both (only shown with -IncludeEqual). Compare-Object is case-insensitive by default - add -CaseSensitive if that's not what you want. $a | Where-Object { $_ -notin $b } is a simpler alternative that returns plain values instead of SideIndicator objects.

Need the same thing in another language?

See the full reference for Python, JavaScript, SQL, Java, Bash, and more.

See every language →

Compare-Object - the built-in cmdlet

$a = @('apple', 'banana', 'cherry')
$b = @('banana', 'date')

Compare-Object $a $b

# InputObject SideIndicator
# ----------- -------------
# cherry      <=
# apple       <=
# date        =>

SideIndicator tells you which side each value came from: <= means the value is only in $a (the first argument, called the reference object), and => means it's only in $b (the difference object). Matching values are omitted from the output entirely unless you add -IncludeEqual.

Filtering to just one side

$aOnly = Compare-Object $a $b |
    Where-Object SideIndicator -eq '<=' |
    Select-Object -ExpandProperty InputObject

$aOnly  # apple, cherry

Piping through Where-Object isolates the <= rows, and Select-Object -ExpandProperty InputObject unwraps the underlying values from the comparison objects Compare-Object returns, giving you back a plain array of strings.

-notin and -in: a simpler operator-based approach

$aOnly = $a | Where-Object { $_ -notin $b }
$aOnly  # apple, cherry

$intersection = $a | Where-Object { $_ -in $b }
$intersection  # banana

-notin and -in read more naturally in a pipeline and return plain values directly rather than SideIndicator objects, at the cost of not distinguishing which original list each unmatched item belongs to (which doesn't matter here since you're already filtering one specific list).

The case-sensitivity trap

$a = @('Apple', 'Banana')
$b = @('apple', 'banana')

Compare-Object $a $b                    # no output - treated as identical
Compare-Object $a $b -CaseSensitive     # shows Apple/apple and Banana/banana as different

Both Compare-Object and the -in/-notin operators are case-insensitive by default in Windows PowerShell and PowerShell 7. If a comparison looks like it's silently ignoring an obvious difference, this is almost always why - add -CaseSensitive to Compare-Object, or use the explicitly case-sensitive operators -cnotin / -cin instead.

Comparing large lists with a HashSet

$setB = [System.Collections.Generic.HashSet[string]]::new($b)
$aOnly = $a | Where-Object { -not $setB.Contains($_) }

Both Compare-Object and the -notin operator effectively scan under the hood, which becomes noticeable on lists in the tens of thousands. Building a .NET HashSet directly and checking .Contains() brings each lookup down to O(1), the same optimization other languages rely on for large-scale comparisons.

Just need the answer, not the code? Paste both lists.

Open Compare 2 Lists →

Frequently Asked Questions

What does Compare-Object return by default?
Compare-Object returns objects with two properties: InputObject (the value) and SideIndicator, which is <= if the value only exists in the first (reference) list, => if it only exists in the second (difference) list, and == for matches, but only when the -IncludeEqual switch is used.
Is Compare-Object case-sensitive?
No, Compare-Object is case-insensitive by default, so 'Apple' and 'apple' are treated as the same value. Add the -CaseSensitive switch if you need an exact-case comparison - this is one of the most common surprises when the tool seems to be ignoring an obvious difference.
How do I get only the items unique to the first list?
Compare-Object $a $b | Where-Object SideIndicator -eq '<=' filters the output down to just the <= rows, which represent values present in $a (the reference object) but not $b (the difference object).
What is the -notin operator and how is it different from Compare-Object?
$a | Where-Object { $_ -notin $b } filters $a down to items that don't appear in $b, similar to Compare-Object's <= rows, but returns the raw values directly instead of objects with a SideIndicator property - useful when you want a plain array back rather than Compare-Object's structured output.
Which is faster for large lists, Compare-Object or -notin?
Neither scales as well as a HashSet-based approach for very large lists, since both effectively do a linear scan per item under the hood. For lists in the tens of thousands or more, build a System.Collections.Generic.HashSet[string] from one list and check .Contains() against it in a loop or with Where-Object - that brings the lookup down to O(1) per item.
How do I compare two lists of objects (not plain strings) in PowerShell?
Pass -Property to tell Compare-Object which property to compare on: Compare-Object $a $b -Property Id. Without -Property, Compare-Object compares whole objects, which usually fails to match custom objects that are logically equal but are different object instances.
How do I get the intersection of two lists in PowerShell?
Compare-Object $a $b -IncludeEqual -ExcludeDifferent returns only the rows present in both lists (SideIndicator ==). Alternatively, $a | Where-Object { $_ -in $b } achieves the same result as a plain array.

Share this tool

Featured on ToolDirs
© 2026 ListDiff

Free online list and text processing tools.