npm.io
0.2.3 • Published 7 months ago

@a11d/equals

Licence
MIT
Version
0.2.3
Deps
0
Size
47 kB
Vulns
0
Weekly
0
Stars
3

@a11d/equals

Value equality for JavaScript. Compares objects, arrays, maps, sets, and functions by structure instead of reference.

import '@a11d/equals'
import { equals } from '@a11d/equals'

const obj1 = { a: 1, b: [1, 2, 3] }
const obj2 = { a: 1, b: [1, 2, 3] }

Object[equals](obj1, obj2) // true
obj1 === obj2 // false

Installation

npm install @a11d/equals

Usage

Two APIs available:

Symbol-based (recommended):

import { equals } from '@a11d/equals'
obj[equals](other)

Global methods (for .equals() syntax or when the symbol conflicts with other libraries):

import '@a11d/equals/global'
obj.equals(other)

Built-in types

Objects — Deep comparison of properties. Requires same constructor and all property values to be equal.

Objects with valueOf()

Compared by their primitive values:

const a = { valueOf: () => 1 }
const b = { valueOf: () => 1 }

Object[equals](a, b) // true

undefined vs absence

Missing properties and undefined values are treated as equal, but null is different:

const obj1 = { a: 1, b: undefined }
const obj2 = { a: 1 }
const obj3 = { a: 1, b: null }

Object[equals](obj1, obj2) // true
Object[equals](obj1, obj3) // false

Prototype-less objects

Objects created with Object.create(null) work fine:

const a = Object.create(null)
a.prop = 'value'

const b = { prop: 'value' }

Object[equals](a, b) // true
Arrays — Element-by-element comparison.

Nested structures are compared deeply:

const arr1 = [1, 2, { key: 'value' }]
const arr2 = [1, 2, { key: 'value' }]

Object[equals](arr1, arr2) // true
Maps & Sets — Entry/element comparison with deep equality.

Values and elements are compared deeply. Note that Map keys are compared by identity:

const map1 = new Map([['a', { id: 1 }], ['b', { id: 2 }]])
const map2 = new Map([['a', { id: 1 }], ['b', { id: 2 }]])

const set1 = new Set([1, { key: 'value' }])
const set2 = new Set([1, { key: 'value' }])

Object[equals](map1, map2) // true
Object[equals](set1, set2) // true
Functions — Functions with identical string representation are equal.
const fn1 = () => 42
const fn2 = () => 42

Object[equals](fn1, fn2) // true

Note: This compares the .toString() output, so functions with the same code but different names or closures may differ.

Custom equality

Implement the equals symbol (or .equals() method with global API):

import { equals } from '@a11d/equals'

class Person {
	constructor(public name: string, public age: number) {}

	[equals](other: unknown): boolean {
		return other instanceof Person
			&& this.name === other.name
			&& this.age === other.age
	}
}

Lit integration

Use hasChanged to trigger re-renders only on structural changes:

import { hasChanged } from '@a11d/equals'

class MyComponent extends Component {
	@property({ type: Object, hasChanged })
	data = { name: 'John', items: [1, 2, 3] }
}

Without this, Lit re-renders whenever you assign a new object reference, even if the content is identical.

Keywords