A Complete Guide to Comparing Values
Table of Contents
- Introduction to Comparison Operators
- Equality Operators
- Relational Operators
- Type Comparison Operators
- Comparison in Different Languages
- Special Comparisons
- String Comparison
- Object Comparison
- Chaining Comparisons
- NaN and Null Comparisons
- Common Pitfalls
- Performance Considerations
- Best Practices
- Interview Questions
Introduction to Comparison Operators
Comparison operators are fundamental building blocks in programming that allow us to compare values and make decisions based on those comparisons. They return a boolean value (true or false) that determines the flow of our programs.
What Are Comparison Operators?
Comparison operators evaluate the relationship between two operands and return a boolean result. They are essential for:
- Making decisions (if statements, loops)
- Sorting and searching algorithms
- Validating user input
- Controlling program flow
// Basic comparison examples let age = 18; let isAdult = age >= 18; // true let canVote = age === 18; // true let isTeen = age < 20 && age > 12; // true
Types of Comparison Operators
| Category | Operators | Purpose |
|---|---|---|
| Equality | ==, ===, !=, !== | Check if values are equal |
| Relational | >, <, >=, <= | Compare numeric/string values |
| Type | typeof, instanceof, is | Check data types |
Equality Operators
Equality operators determine if two values are equal. Different languages handle equality differently.
Loose Equality (==)
Loose equality performs type coercion before comparison.
// JavaScript - loose equality (==)
console.log(5 == "5"); // true (string converted to number)
console.log(0 == false); // true (boolean converted to number)
console.log(null == undefined); // true
console.log("" == 0); // true
console.log([] == false); // true (complex rules!)
console.log({} == {}); // false (different references)
// PHP - loose equality (==) var_dump(5 == "5"); // bool(true) var_dump(0 == false); // bool(true) var_dump(null == false); // bool(true) - careful!
Strict Equality (===)
Strict equality checks both value and type without coercion.
// JavaScript - strict equality (===) console.log(5 === "5"); // false (different types) console.log(0 === false); // false (different types) console.log(null === undefined); // false console.log(5 === 5); // true console.log(NaN === NaN); // false (NaN is not equal to itself)
# Python - no loose equality, only strict print(5 == "5") # False (no coercion) print(5 == 5) # True print(5 == 5.0) # True (numeric types considered equal)
Inequality Operators
// JavaScript console.log(5 != "5"); // false (loose inequality) console.log(5 !== "5"); // true (strict inequality) console.log(5 != 6); // true
# Python print(5 != "5") # True print(5 != 5) # False print(5 != 5.0) # False
Language Comparison Table
| Language | Loose Equality | Strict Equality | Notes |
|---|---|---|---|
| JavaScript | == | === | Coercion rules can be complex |
| Python | == only | N/A | No type coercion |
| Java | == for primitives | equals() for objects | Different approach |
| PHP | == | === | Similar to JavaScript |
| C# | == | Equals() | Type-safe |
| Ruby | == | eql? | Different semantics |
Relational Operators
Relational operators compare the relative order of values.
Greater Than and Less Than
// JavaScript
console.log(10 > 5); // true
console.log(10 < 5); // false
console.log(10 >= 10); // true
console.log(10 <= 5); // false
// With type coercion
console.log("10" > 5); // true (string converted to number)
console.log("apple" > "banana"); // false (lexicographic comparison)
# Python
print(10 > 5) # True
print(10 < 5) # False
print(10 >= 10) # True
print(10 <= 5) # False
# Python raises TypeError for incompatible types
# print("10" > 5) # TypeError!
// Java int a = 10; int b = 5; System.out.println(a > b); // true System.out.println(a < b); // false // Strings use compareTo method String s1 = "apple"; String s2 = "banana"; System.out.println(s1.compareTo(s2) > 0); // false
Numeric Comparison Examples
// Edge cases console.log(Infinity > 1000000); // true console.log(Infinity > Infinity); // false console.log(-Infinity < -1000000); // true console.log(NaN > 5); // false console.log(NaN < 5); // false console.log(NaN >= NaN); // false console.log(NaN <= NaN); // false
Type Comparison Operators
typeof Operator
// JavaScript - typeof returns a string
console.log(typeof 42); // "number"
console.log(typeof "Hello"); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" (quirks!)
console.log(typeof {}); // "object"
console.log(typeof []); // "object"
console.log(typeof function(){}); // "function"
# Python - type() returns type object
print(type(42)) # <class 'int'>
print(type("Hello")) # <class 'str'>
print(type(True)) # <class 'bool'>
print(type(None)) # <class 'NoneType'>
print(type([])) # <class 'list'>
print(type({})) # <class 'dict'>
instanceof Operator
// JavaScript - checks prototype chain
class Animal {}
class Dog extends Animal {}
let dog = new Dog();
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true
console.log([] instanceof Array); // true
console.log([] instanceof Object); // true
// Java - instanceof operator
String text = "Hello";
System.out.println(text instanceof String); // true
System.out.println(text instanceof Object); // true
// With inheritance
class Animal {}
class Dog extends Animal {}
Dog dog = new Dog();
System.out.println(dog instanceof Dog); // true
System.out.println(dog instanceof Animal); // true
is and is not (Python)
# Python - identity comparison a = [1, 2, 3] b = [1, 2, 3] c = a print(a is b) # False (different objects) print(a is c) # True (same object) print(a is not b) # True # For singletons print(None is None) # True print(True is True) # True print(False is False) # True
Comparison in Different Languages
JavaScript Comparison
// JavaScript - comprehensive comparison
console.log(5 == "5"); // true
console.log(5 === "5"); // false
// Object comparison
let obj1 = { value: 5 };
let obj2 = { value: 5 };
console.log(obj1 == obj2); // false (different references)
console.log(obj1 === obj2); // false
// Array comparison
let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
console.log(arr1 == arr2); // false
Python Comparison
# Python - clean and predictable
print(5 == "5") # False
print(5 == 5.0) # True
print(5 is 5) # True (small integers cached)
# Object comparison
class Person:
def __init__(self, name):
self.name = name
def __eq__(self, other):
if isinstance(other, Person):
return self.name == other.name
return False
p1 = Person("Alice")
p2 = Person("Alice")
print(p1 == p2) # True (custom __eq__)
print(p1 is p2) # False (different objects)
Java Comparison
// Java - primitives vs objects
int a = 5;
int b = 5;
System.out.println(a == b); // true
String s1 = new String("Hello");
String s2 = new String("Hello");
System.out.println(s1 == s2); // false (different references)
System.out.println(s1.equals(s2)); // true
// Integer caching
Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1 == i2); // true (cached)
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4); // false (not cached)
C# Comparison
// C# - type-safe comparisons
int a = 5;
int b = 5;
Console.WriteLine(a == b); // true
string s1 = "Hello";
string s2 = "Hello";
Console.WriteLine(s1 == s2); // true (string interning)
Console.WriteLine(object.ReferenceEquals(s1, s2)); // true
// Custom equality
class Person
{
public string Name { get; set; }
public override bool Equals(object obj)
{
return obj is Person other && Name == other.Name;
}
}
PHP Comparison
// PHP - complex comparison rules
var_dump(5 == "5"); // bool(true)
var_dump(5 === "5"); // bool(false)
var_dump(0 == false); // bool(true)
var_dump(0 === false); // bool(false)
// String comparison
var_dump("10" > "2"); // bool(false) (lexicographic)
var_dump(10 > "2"); // bool(true) (numeric)
Special Comparisons
NaN Comparisons
NaN (Not a Number) behaves uniquely in comparisons.
// JavaScript
console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
console.log(NaN > 0); // false
console.log(NaN < 0); // false
console.log(isNaN(NaN)); // true
console.log(Number.isNaN(NaN)); // true
// Number.isNaN is more reliable
console.log(Number.isNaN("abc")); // false
console.log(isNaN("abc")); // true (coerces to NaN)
# Python
import math
print(float('nan') == float('nan')) # False
print(math.isnan(float('nan'))) # True
Null and Undefined Comparisons
// JavaScript console.log(null == undefined); // true console.log(null === undefined); // false console.log(null == 0); // false console.log(null > 0); // false console.log(null < 0); // false console.log(null >= 0); // true (weird!) console.log(null <= 0); // true (weird!)
# Python print(None == None) # True print(None is None) # True # None compares only to None print(None == 0) # False
Infinity Comparisons
// JavaScript console.log(Infinity > 1000000); // true console.log(Infinity > Infinity); // false console.log(Infinity >= Infinity); // true console.log(-Infinity < -1000000); // true console.log(1 / 0); // Infinity console.log(-1 / 0); // -Infinity
String Comparison
Lexicographic Comparison
Strings are compared character by character based on their Unicode code points.
// JavaScript
console.log("apple" < "banana"); // true ('a' < 'b')
console.log("Apple" < "apple"); // true ('A' (65) < 'a' (97))
console.log("10" < "2"); // true ('1' < '2')
console.log("z" > "a"); // true
// Locale-aware comparison
console.log("ä".localeCompare("a")); // Usually 1 (ä > a)
# Python
print("apple" < "banana") # True
print("Apple" < "apple") # True
print("10" < "2") # True (string comparison)
print(10 < 2) # False (numeric comparison)
# Case-insensitive comparison
print("APPLE".lower() == "apple".lower()) # True
// Java
String s1 = "apple";
String s2 = "banana";
System.out.println(s1.compareTo(s2) < 0); // true
System.out.println(s1.equalsIgnoreCase("APPLE")); // true
String Comparison Rules
// Unicode code point order
console.log("a" < "b"); // true
console.log("á" < "b"); // true (U+00E1 < U+0062? Actually 225 > 98, so false!)
console.log("z" < "ä"); // false (U+007A < U+00E4? 122 < 228, so true!)
// The actual Unicode order is based on code points, not alphabetical order
Object Comparison
Reference vs Value Comparison
// JavaScript - objects compared by reference
let obj1 = { name: "Alice" };
let obj2 = { name: "Alice" };
let obj3 = obj1;
console.log(obj1 === obj2); // false (different references)
console.log(obj1 === obj3); // true (same reference)
// Arrays are objects
let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
console.log(arr1 === arr2); // false
# Python - objects compared by identity by default
class Person:
def __init__(self, name):
self.name = name
p1 = Person("Alice")
p2 = Person("Alice")
p3 = p1
print(p1 is p2) # False
print(p1 is p3) # True
print(p1 == p2) # False (unless __eq__ is defined)
Implementing Custom Equality
# Python - custom equality with __eq__
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
if isinstance(other, Person):
return self.name == other.name and self.age == other.age
return False
def __hash__(self):
return hash((self.name, self.age))
p1 = Person("Alice", 30)
p2 = Person("Alice", 30)
print(p1 == p2) # True
print(p1 is p2) # False
// JavaScript - custom equality with method
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
equals(other) {
return other instanceof Person &&
this.name === other.name &&
this.age === other.age;
}
}
let p1 = new Person("Alice", 30);
let p2 = new Person("Alice", 30);
console.log(p1.equals(p2)); // true
Deep Comparison
// JavaScript - deep comparison function
function deepEqual(a, b) {
if (a === b) return true;
if (typeof a !== 'object' || typeof b !== 'object' ||
a === null || b === null) return false;
let keysA = Object.keys(a);
let keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
for (let key of keysA) {
if (!keysB.includes(key) || !deepEqual(a[key], b[key])) {
return false;
}
}
return true;
}
let obj1 = { a: 1, b: { c: 2 } };
let obj2 = { a: 1, b: { c: 2 } };
console.log(deepEqual(obj1, obj2)); // true
Chaining Comparisons
Chaining in Different Languages
# Python - supports comparison chaining x = 5 print(1 < x < 10) # True print(1 < x > 3) # True print(1 < x < 3) # False # Multiple chaining a = 5 b = 10 c = 15 print(a < b < c) # True print(a < b > c) # False
// JavaScript - doesn't support chaining the same way let x = 5; console.log(1 < x && x < 10); // true (must use &&) console.log(1 < x < 10); // true? Actually (1 < x) is true, true < 10? true becomes 1, so 1 < 10 is true - but not what you want!
Logical Operators for Chaining
// JavaScript - use && for chaining let age = 25; let isAdult = age >= 18 && age <= 65; // true // For multiple comparisons let score = 85; let grade = score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : 'F';
# Python - elegant chaining
age = 25
is_adult = 18 <= age <= 65 # True
# Range checking
temperature = 75
if 32 <= temperature <= 212:
print("Water is liquid")
NaN and Null Comparisons
NaN Comparisons
// JavaScript - NaN is not equal to anything
console.log(NaN === NaN); // false
console.log(NaN == NaN); // false
console.log(NaN > 0); // false
console.log(NaN < 0); // false
console.log(NaN >= NaN); // false
console.log(NaN <= NaN); // false
// Checking for NaN
console.log(isNaN(NaN)); // true
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN("abc")); // false (doesn't coerce)
// Using Object.is (ES6)
console.log(Object.is(NaN, NaN)); // true
# Python
import math
print(float('nan') == float('nan')) # False
print(math.isnan(float('nan'))) # True
Null and Undefined Comparisons
// JavaScript console.log(null == undefined); // true console.log(null === undefined); // false console.log(null == 0); // false console.log(null > 0); // false console.log(null < 0); // false console.log(null >= 0); // true (converts to 0) console.log(null <= 0); // true // undefined with numbers console.log(undefined == 0); // false console.log(undefined > 0); // false console.log(undefined < 0); // false console.log(undefined >= 0); // false console.log(undefined <= 0); // false
# Python print(None == 0) # False print(None is None) # True print(None == False) # False
Common Pitfalls
1. Type Coercion in Loose Equality
// JavaScript - unexpected behavior
console.log([] == false); // true
console.log([] == ![]); // true
console.log("" == false); // true
console.log("0" == false); // true
console.log("0" == 0); // true
console.log(false == "false"); // false
console.log({} == {}); // false
// Why [] == ![] is true:
// ![] = false
// [] == false
// [] == 0 (false converted to 0)
// "" == 0 ([] converted to "")
// 0 == 0 (true)
2. Floating Point Precision
// JavaScript console.log(0.1 + 0.2 === 0.3); // false console.log(0.1 + 0.2); // 0.30000000000000004 // Solution: use tolerance const EPSILON = 0.000001; console.log(Math.abs((0.1 + 0.2) - 0.3) < EPSILON); // true
# Python
print(0.1 + 0.2 == 0.3) # False
print(0.1 + 0.2) # 0.30000000000000004
# Solution
from decimal import Decimal
print(Decimal('0.1') + Decimal('0.2') == Decimal('0.3')) # True
3. String vs Number Comparisons
// JavaScript
console.log("10" > "2"); // false (lexicographic: '1' < '2')
console.log(10 > "2"); // true (numeric: 10 > 2)
console.log("10" > 2); // true (converts to number)
# Python - TypeError, no automatic coercion
# print("10" > 2) # TypeError!
print(int("10") > 2) # True
print("10" > "2") # False (lexicographic)
4. NaN Comparison Pitfall
// JavaScript
let result = Math.sqrt(-1); // NaN
if (result === NaN) { // Never true!
console.log("This never runs");
}
// Correct way
if (isNaN(result)) {
console.log("Result is NaN");
}
5. Null and 0 Comparison
// JavaScript
let value = null;
if (value == 0) { // false
// Won't execute
}
if (value >= 0) { // true! (null converted to 0)
// Will execute
}
Performance Considerations
Comparison Cost
// Primitive comparisons are fast
let a = 5;
let b = 10;
a === b; // Fast
// Object comparisons require property checks
let obj1 = { x: 1, y: 2 };
let obj2 = { x: 1, y: 2 };
obj1 === obj2; // Fast (reference comparison)
deepEqual(obj1, obj2); // Slow (property iteration)
// Optimize deep comparisons
function optimizedDeepEqual(a, b) {
if (a === b) return true;
if (typeof a !== 'object' || typeof b !== 'object') return false;
let keysA = Object.keys(a);
let keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
// Use for loop instead of for...in for better performance
for (let i = 0; i < keysA.length; i++) {
let key = keysA[i];
if (!b.hasOwnProperty(key) || !deepEqual(a[key], b[key])) {
return false;
}
}
return true;
}
Short-Circuit Optimization
// Order matters for performance
// Put cheaper comparisons first
if (isExpensiveCheck() && simpleCheck()) { // Bad
}
if (simpleCheck() && isExpensiveCheck()) { // Good
}
Best Practices
1. Use Strict Equality
// ❌ Bad
if (age == 18) { }
// ✅ Good
if (age === 18) { }
2. Explicit Type Conversion
// ❌ Bad
if (input == 5) { }
// ✅ Good
let num = Number(input);
if (num === 5) { }
# ❌ Bad if input == 5: pass # ✅ Good num = int(input) if num == 5: pass
3. Check for NaN Properly
// ❌ Bad
if (value === NaN) { }
// ✅ Good
if (Number.isNaN(value)) { }
4. Use Meaningful Variable Names
// ❌ Bad
if (a > b) { }
// ✅ Good
if (userAge >= LEGAL_DRINKING_AGE) { }
5. Handle Edge Cases
function compareNumbers(a, b) {
// Handle NaN
if (Number.isNaN(a) || Number.isNaN(b)) {
return false;
}
// Handle Infinity
if (a === Infinity && b === Infinity) return true;
if (a === -Infinity && b === -Infinity) return true;
return a === b;
}
6. Use Object.is for Special Cases
// Object.is handles NaN and -0 correctly console.log(Object.is(NaN, NaN)); // true console.log(Object.is(-0, +0)); // false console.log(-0 === +0); // true
Interview Questions
Q1: What's the difference between == and === in JavaScript?
// Answer: == performs type coercion, === does not console.log(5 == "5"); // true (coerces string to number) console.log(5 === "5"); // false (different types) console.log(false == 0); // true console.log(false === 0); // false // Best practice: always use ===
Q2: What will this output?
console.log([] == false); // true
console.log([] == ![]); // true
console.log("" == false); // true
console.log("" == 0); // true
console.log("0" == false); // true
console.log("0" == 0); // true
console.log(false == "false"); // false
Q3: How would you compare two objects in JavaScript?
// Shallow comparison
function shallowEqual(obj1, obj2) {
if (obj1 === obj2) return true;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (let key of keys1) {
if (obj1[key] !== obj2[key]) return false;
}
return true;
}
// Deep comparison
function deepEqual(obj1, obj2) {
if (obj1 === obj2) return true;
if (typeof obj1 !== 'object' || typeof obj2 !== 'object' ||
obj1 === null || obj2 === null) return false;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (let key of keys1) {
if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
return false;
}
}
return true;
}
Q4: Why does NaN not equal itself?
// Answer: By IEEE 754 specification, NaN is not equal to anything
// This allows detection of invalid operations
let result = Math.sqrt(-1); // NaN
if (result !== result) { // true
console.log("NaN detected");
}
// Modern way
if (Number.isNaN(result)) {
console.log("NaN detected");
}
Q5: What's the output of this code?
console.log(1 < 2 < 3); // true (1 < 2 = true, true < 3 = 1 < 3 = true) console.log(3 > 2 > 1); // false (3 > 2 = true, true > 1 = 1 > 1 = false)
Q6: Compare strings in different languages
// JavaScript
console.log("apple" < "banana"); // true
console.log("Apple" < "apple"); // true (Uppercase ASCII values are smaller)
// Python
// print("Apple" < "apple") # True (same reason)
Q7: How to compare numbers with tolerance for floating point?
function almostEqual(a, b, epsilon = 0.000001) {
return Math.abs(a - b) < epsilon;
}
console.log(almostEqual(0.1 + 0.2, 0.3)); // true
Q8: What will this Python code output?
a = 256 b = 256 print(a is b) # True (cached) print(a == b) # True c = 257 d = 257 print(c is d) # False (not cached) print(c == d) # True
Conclusion
Comparison operators are fundamental to programming logic and decision-making.
Key Takeaways
| Operator Type | Operators | Best Practice |
|---|---|---|
| Equality | ==, ===, !=, !== | Use strict equality (===) in JavaScript, avoid loose equality |
| Relational | >, <, >=, <= | Be aware of type coercion; convert types explicitly |
| Type | typeof, instanceof, is | Use appropriate type checking for your language |
Best Practices Summary
- Use strict equality (
===) over loose equality (==) - Explicitly convert types before comparison
- Check for NaN using
Number.isNaN()orisNaN() - Be aware of floating point precision issues
- Understand chaining behavior in different languages
- Implement custom equality for objects when needed
- Consider performance in tight loops
- Handle edge cases (null, undefined, NaN, Infinity)
Quick Reference Card
// JavaScript best practices
// Equality
if (value === 5) { } // Strict equality
if (value !== null) { } // Strict inequality
// Type checking
if (typeof value === 'number') { }
if (Array.isArray(value)) { }
// NaN checking
if (Number.isNaN(value)) { }
// Floating point
if (Math.abs(a - b) < 1e-10) { }
// Null/undefined
if (value == null) { } // Checks both null and undefined
// Chaining (use &&)
if (min <= value && value <= max) { }
Understanding comparison operators is essential for writing correct and predictable code. Master these concepts to become a more effective programmer!