Search code examples
javascriptlodash

Compare equality of instances of a class with private property


How to make a class with private properties so lodash isEqual returns false when comparing any 2 instances with different items in the private properties?

For example, in the following code isEqual comparison of the 2 instances of the Bobcat class with different items results in true. I want to have false. Is there any Map method missing or anything?

const _ = require('lodash')

class Bobcat {
  #items;

  constructor() {
    this.#items = new Map()
  }

  set(key, value) {
    return this.#items.set(key, value)
  }

  get(key) {
    return this.#items.get(key)
  }

  has(key) {
    return this.#items.has(key)
  }

  delete(key) {
    return this.#items.delete(key)
  }

  clear() {
    this.#items.clear()
  }

  keys() {
    return this.#items.keys()
  }

  values() {
    return this.#items.values()
  }

  entries() {
    return this.#items.entries()
  }

  forEach(callbackFn, thisArg) {
    return this.#items.forEach(callbackFn, thisArg)
  }

  [Symbol.iterator]() {
    return this.#items[Symbol.iterator]()
  }
}

const cat1 = new Bobcat()
cat1.set('a', 1)

const cat2 = new Bobcat()
cat2.set('a', 2)

console.log(_.isEqual(cat1, cat2))
// true

Another requirement is to have the comparison outside of the class. No comparison method should be added to the class itself. For example, I don't want

class Bobcat {
  isEqual(otherCat) {
    ... 
  }
}

or any specific to lodash functionality like

class Bobcat {
  [Symbol.for('lodash.isEqual')](otherCat) {
    ... 
  }
}

Solution

  • Customizing toStringTag to return Map solves the issue for me without changing the downstream code on the isEqual comparison.

      get [Symbol.toStringTag]() {
        return 'Map'
      }
    

    Entire code

    const _ = require('lodash')
    
    class Bobcat {
      #items
    
      constructor() {
        this.#items = new Map()
      }
    
      set(key, value) {
        return this.#items.set(key, value)
      }
    
      get(key) {
        return this.#items.get(key)
      }
    
      has(key) {
        return this.#items.has(key)
      }
    
      delete(key) {
        return this.#items.delete(key)
      }
    
      clear() {
        this.#items.clear()
      }
    
      keys() {
        return this.#items.keys()
      }
    
      values() {
        return this.#items.values()
      }
    
      entries() {
        return this.#items.entries()
      }
    
      forEach(callbackFn, thisArg) {
        return this.#items.forEach(callbackFn, thisArg)
      }
    
      [Symbol.iterator]() {
        return this.#items[Symbol.iterator]()
      }
    
      get size() {
        return this.#items.size
      }
    
      get [Symbol.toStringTag]() {
        return 'Map'
      }
    }
    
    const cat1 = new Bobcat()
    cat1.set('a', 1)
    
    const cat2 = new Bobcat()
    cat2.set('a', 1)
    
    // true
    console.log('equal1', _.isEqual(cat1, cat2))
    
    cat2.set('a', 2)
    
    // false
    console.log('equal2', _.isEqual(cat1, cat2))