Skip to content

DiffUsing Method

The diffUsing() method compares the collection against another array or collection using a custom callback function. This allows for complex comparison logic when determining which items should be considered different.

Basic Syntax

typescript
const callback: (a: T, b: T) => boolean
collect(items).diffUsing(array, callback)

Examples

Basic Usage

typescript
import { collect } from 'ts-collect'

interface Item {
  id: number
  value: string
}

const collection = collect<Item>([
  { id: 1, value: 'a' },
  { id: 2, value: 'b' },
  { id: 3, value: 'c' }
])

const comparison = [
  { id: 1, value: 'a' },
  { id: 2, value: 'x' },
  { id: 4, value: 'd' }
]

const diff = collection.diffUsing(comparison, (a, b) => a.id === b.id)
console.log(diff.all())
// [
//   { id: 3, value: 'c' }
// ]

Complex Comparisons

typescript
interface User {
  id: number
  email: string
  permissions: string[]
}

const currentUsers = collect<User>([
  { id: 1, email: 'john@example.com', permissions: ['read', 'write'] },
  { id: 2, email: 'jane@example.com', permissions: ['read'] },
  { id: 3, email: 'bob@example.com', permissions: ['admin'] }
])

const newUsers = [
  { id: 1, email: 'john@example.com', permissions: ['read'] },
  { id: 2, email: 'jane@example.com', permissions: ['read'] },
  { id: 4, email: 'alice@example.com', permissions: ['read'] }
]

// Compare users based on both ID and permissions
const diff = currentUsers.diffUsing(newUsers, (a, b) =>
  a.id === b.id
  && JSON.stringify(a.permissions) === JSON.stringify(b.permissions))

console.log(diff.all())
// [
//   { id: 1, email: 'john@example.com', permissions: ['read', 'write'] },
//   { id: 3, email: 'bob@example.com', permissions: ['admin'] }
// ]

Real-world Examples

Version Comparison

typescript
interface Version {
  major: number
  minor: number
  patch: number
  label?: string
}

const installedVersions = collect<Version>([
  { major: 1, minor: 0, patch: 0 },
  { major: 1, minor: 1, patch: 0 },
  { major: 2, minor: 0, patch: 0, label: 'beta' }
])

const availableVersions = [
  { major: 1, minor: 0, patch: 0 },
  { major: 1, minor: 1, patch: 1 },
  { major: 2, minor: 0, patch: 0 }
]

const needsUpdate = installedVersions.diffUsing(availableVersions, (a, b) => {
  return a.major === b.major
    && a.minor === b.minor
    && a.patch === b.patch
    && (!a.label === !b.label)
})

console.log(needsUpdate.all())
// [
//   { major: 2, minor: 0, patch: 0, label: 'beta' }
// ]

Document Change Detection

typescript
interface Document {
  id: string
  title: string
  content: string
  metadata: {
    author: string
    lastModified: string
  }
}

const originalDocs = collect<Document>([
  {
    id: '1',
    title: 'Hello',
    content: 'World',
    metadata: { author: 'John', lastModified: '2024-01-01' }
  },
  {
    id: '2',
    title: 'Test',
    content: 'Content',
    metadata: { author: 'Jane', lastModified: '2024-01-02' }
  }
])

const updatedDocs = [
  {
    id: '1',
    title: 'Hello',
    content: 'Updated World',
    metadata: { author: 'John', lastModified: '2024-01-03' }
  },
  {
    id: '2',
    title: 'Test',
    content: 'Content',
    metadata: { author: 'Jane', lastModified: '2024-01-02' }
  }
]

// Compare documents ignoring lastModified date
const changedDocs = originalDocs.diffUsing(updatedDocs, (a, b) => {
  return a.id === b.id
    && a.content === b.content
    && a.metadata.author === b.metadata.author
})

console.log(changedDocs.all())
// [
//   {
//     id: '1',
//     title: 'Hello',
//     content: 'World',
//     metadata: { author: 'John', lastModified: '2024-01-01' }
//   }
// ]

Advanced Usage

Deep Object Comparison

typescript
interface ConfigItem {
  key: string
  settings: {
    enabled: boolean
    options: string[]
    nested?: {
      value: number
    }
  }
}

const originalConfig = collect<ConfigItem>([
  {
    key: 'feature1',
    settings: {
      enabled: true,
      options: ['a', 'b'],
      nested: { value: 1 }
    }
  },
  {
    key: 'feature2',
    settings: {
      enabled: false,
      options: ['x']
    }
  }
])

const newConfig = [
  {
    key: 'feature1',
    settings: {
      enabled: true,
      options: ['a', 'b', 'c'],
      nested: { value: 1 }
    }
  },
  {
    key: 'feature2',
    settings: {
      enabled: false,
      options: ['x']
    }
  }
]

// Deep comparison ignoring specific nested properties
const diff = originalConfig.diffUsing(newConfig, (a, b) => {
  return a.key === b.key
    && a.settings.enabled === b.settings.enabled
    && JSON.stringify(a.settings.nested) === JSON.stringify(b.settings.nested)
})

console.log(diff.all())
// Shows items where the comparison returned false

Type Safety

typescript
interface TypedItem<T> {
  id: number
  value: T
}

const numbers = collect<TypedItem<number>>([
  { id: 1, value: 100 },
  { id: 2, value: 200 }
])

const comparison = [
  { id: 1, value: 100 },
  { id: 2, value: 300 }
]

// TypeScript ensures type safety in the callback
const diff = numbers.diffUsing(comparison, (a, b) => {
  return a.id === b.id && a.value === b.value
})

Return Value

Returns a new Collection instance containing the items from the original collection that are considered different based on the callback function's comparison logic.

Released under the MIT License.