# equals

Deep equality comparison for SurrealDB values and JavaScript types.

The `equals()` function performs deep equality comparison for values, including SurrealDB-specific types that may not compare correctly with JavaScript's `===` operator.

**Import:**
```ts
import { equals } from 'surrealdb';
```

**Source:** [utils/equals.ts](https://github.com/surrealdb/surrealdb.js/blob/main/packages/sdk/src/utils/equals.ts)

## Function signature

```ts
function equals(x: unknown, y: unknown): boolean
```

### Parameters
<table>
    <thead>
        <tr>
            <th>Parameter</th>
            <th>Type</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><code>x</code> <label label="required" /></td>
            <td><code>unknown</code></td>
            <td>First value to compare.</td>
        </tr>
        <tr>
            <td><code>y</code> <label label="required" /></td>
            <td><code>unknown</code></td>
            <td>Second value to compare.</td>
        </tr>
    </tbody>
</table>

### Returns
`boolean` - `true` if values are deeply equal, `false` otherwise

## What it compares

The `equals()` function correctly handles:
- **Primitives** - strings, numbers, booleans, null, undefined
- **SurrealDB Types** - RecordId, DateTime, Duration, Decimal, Uuid, etc.
- **Dates** - JavaScript Date objects
- **Regular Expressions** - RegExp patterns
- **Objects** - Deep object comparison
- **Arrays** - Deep array comparison
- **Mixed types** - Proper handling of bigint/number comparisons

## Examples

### Primitive comparisons

```ts
import { equals } from 'surrealdb';

console.log(equals(42, 42)); // true
console.log(equals('hello', 'hello')); // true
console.log(equals(true, false)); // false
console.log(equals(null, null)); // true
```

### SurrealDB type comparisons

```ts
import { RecordId, DateTime, Uuid, Decimal, equals } from 'surrealdb';

// RecordId comparison
const id1 = new RecordId('users', 'john');
const id2 = new RecordId('users', 'john');
const id3 = new RecordId('users', 'jane');

console.log(equals(id1, id2)); // true
console.log(equals(id1, id3)); // false

// DateTime comparison (including nanoseconds)
const dt1 = new DateTime('2024-01-15T12:00:00.123456789Z');
const dt2 = new DateTime('2024-01-15T12:00:00.123456789Z');

console.log(equals(dt1, dt2)); // true

// Uuid comparison
const uuid1 = Uuid.parse('550e8400-e29b-41d4-a716-446655440000');
const uuid2 = Uuid.parse('550e8400-e29b-41d4-a716-446655440000');

console.log(equals(uuid1, uuid2)); // true

// Decimal comparison (arbitrary precision)
const dec1 = new Decimal('19.99');
const dec2 = new Decimal('19.99');

console.log(equals(dec1, dec2)); // true
```

### Object comparisons

```ts
const obj1 = { name: 'John', age: 30 };
const obj2 = { name: 'John', age: 30 };
const obj3 = { name: 'John', age: 31 };

console.log(equals(obj1, obj2)); // true
console.log(equals(obj1, obj3)); // false

// Deep nested objects
const deep1 = {
    user: {
        profile: {
            name: 'John'
        }
    }
};
const deep2 = {
    user: {
        profile: {
            name: 'John'
        }
    }
};

console.log(equals(deep1, deep2)); // true
```

### Array comparisons

```ts
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];
const arr3 = [1, 2, 4];

console.log(equals(arr1, arr2)); // true
console.log(equals(arr1, arr3)); // false

// Arrays with SurrealDB types
const ids1 = [
    new RecordId('users', 'john'),
    new RecordId('users', 'jane')
];
const ids2 = [
    new RecordId('users', 'john'),
    new RecordId('users', 'jane')
];

console.log(equals(ids1, ids2)); // true
```

### Mixed type comparisons

```ts
// bigint and number comparison
console.log(equals(42n, 42)); // true
console.log(equals(42, 42n)); // true

// Date comparison
const date1 = new Date('2024-01-15');
const date2 = new Date('2024-01-15');

console.log(equals(date1, date2)); // true
```

## Use cases

### Checking record existence

```ts
async function hasRecord(recordId: RecordId): Promise<boolean> {
    const records = await db.select(new Table(recordId.tb));
    
    return records.some(record => equals(record.id, recordId));
}

const exists = await hasRecord(new RecordId('users', 'john'));
```

### Deduplication

```ts
function deduplicate<T>(items: T[]): T[] {
    const unique: T[] = [];
    
    for (const item of items) {
        if (!unique.some(u => equals(u, item))) {
            unique.push(item);
        }
    }
    
    return unique;
}

const recordIds = [
    new RecordId('users', 'john'),
    new RecordId('users', 'jane'),
    new RecordId('users', 'john'), // Duplicate
];

const uniqueIds = deduplicate(recordIds);
console.log(uniqueIds.length); // 2
```

### Change detection

```ts
function hasChanged<T>(before: T, after: T): boolean {
    return !equals(before, after);
}

const originalUser = await db.select(userId);
// ... user makes changes ...
const updatedUser = await db.select(userId);

if (hasChanged(originalUser, updatedUser)) {
    console.log('User data was modified');
}
```

### Array difference

```ts
function findNewItems<T>(oldList: T[], newList: T[]): T[] {
    return newList.filter(newItem => 
        !oldList.some(oldItem => equals(oldItem, newItem))
    );
}

const oldTags = ['javascript', 'typescript'];
const newTags = ['javascript', 'typescript', 'react'];

const added = findNewItems(oldTags, newTags);
console.log(added); // ['react']
```

### Caching / memoization

```ts
class Cache<K, V> {
    private cache = new Map<string, { key: K; value: V }>();
    
    set(key: K, value: V): void {
        const entry = { key, value };
        this.cache.set(JSON.stringify(key), entry);
    }
    
    get(key: K): V | undefined {
        for (const entry of this.cache.values()) {
            if (equals(entry.key, key)) {
                return entry.value;
            }
        }
        return undefined;
    }
}

// Use with complex keys
const cache = new Cache<RecordId, User>();
const userId = new RecordId('users', 'john');

cache.set(userId, userData);
const cached = cache.get(new RecordId('users', 'john')); // Found!
```

### Record comparison

```ts
interface User {
    id: RecordId;
    name: string;
    email: string;
    settings: {
        theme: string;
        notifications: boolean;
    };
}

const user1: User = await db.select(new RecordId('users', 'john'));
const user2: User = await db.select(new RecordId('users', 'john'));

// Deep comparison including nested objects and RecordId
console.log(equals(user1, user2)); // true
```

## Why not use `===` or `==`?

JavaScript's equality operators don't work correctly for:

```ts
// Problem 1: Object references
const obj1 = { name: 'John' };
const obj2 = { name: 'John' };
console.log(obj1 === obj2); // false (different references)
console.log(equals(obj1, obj2)); // true (same content)

// Problem 2: SurrealDB types
const id1 = new RecordId('users', 'john');
const id2 = new RecordId('users', 'john');
console.log(id1 === id2); // false (different instances)
console.log(equals(id1, id2)); // true (same value)

// Problem 3: Date comparison
const date1 = new Date('2024-01-15');
const date2 = new Date('2024-01-15');
console.log(date1 === date2); // false
console.log(equals(date1, date2)); // true

// Problem 4: Arrays
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];
console.log(arr1 === arr2); // false
console.log(equals(arr1, arr2)); // true
```

## Best practices

### 1. Use for value comparisons

```ts
// Good: Deep equality
if (equals(recordId1, recordId2)) {
    // Same record
}

// Avoid: Reference equality (wrong for objects)
if (recordId1 === recordId2) {
    // Only true if same instance
}
```

### 2. Use for complex type comparisons

```ts
// Good: Proper comparison
const sameDateTime = equals(
    new DateTime('2024-01-15T12:00:00.123456789Z'),
    new DateTime('2024-01-15T12:00:00.123456789Z')
); // true

// Avoid: Direct comparison
const wrong = dt1 === dt2; // false (different instances)
```

### 3. Prefer built-in `.equals()` for single types

```ts
const id1 = new RecordId('users', 'john');
const id2 = new RecordId('users', 'john');

// Both are fine:
console.log(equals(id1, id2)); // true
console.log(id1.equals(id2)); // true

// Use id1.equals(id2) when you know both are RecordId
// Use equals(id1, id2) for generic comparisons
```

## See also

- [Data types](/docs/reference/javascript/api/values/) - SurrealDB data types
- [RecordId.equals()](/docs/reference/javascript/api/values/record-id.md#equals) - RecordId-specific comparison
- [DateTime.equals()](/docs/reference/javascript/api/values/datetime.md#equals) - DateTime-specific comparison
