Search code examples
javascriptecmascript-6lodash

Comparing arrays of arrays with Lodash


I can't understand how to pull arrays from one array out of another.

I tried using plain JavaScript (ES6):

let openTiles = [[1, 1], [2, 2], [1, 3]]
let usedTiles = [[1, 1], [2, 2]]
openTiles = openTiles.filter((item) => !usedTiles.includes(item))

I expected the final openTiles to be: [[1, 3]] but it is unchanged. The problem is, the code above uses JavaScript's standard comparison (===) which can't compare one array with another. Lodash has the _.isEqual() function but I can't understand how to implement it.

I tried:

openTiles = openTiles.filter((item) => {
    return usedTiles.every((el) => {
        _.isEqual(item, el)
    })
})

but that gives me an empty array. I would like to see how people incorporate Lodash's _.isEqual() function so that all the arrays in usedTiles can be removed from openTiles.


Solution

  • Rather than trying to write a general-purpose object- or array-equals function, or using the ones from lodash, Ramda, underscore, etc, you can write one specific to your type. If your tiles are just two-element arrays, then simply write an equals function that reflects that. Then you can use it in some:

    const tilesEqual = (a) => (b) => a[0] == b[0] && a[1] == b[1]
    const removeTiles = (open, used) => open.filter(
      tile => !used.some(tilesEqual(tile))
    )
    
    let openTiles = [[1, 1], [2, 2], [1, 3]]
    let usedTiles = [[1, 1], [2, 2]]
    
    console.log(removeTiles(openTiles, usedTiles))

    As to why your code above didn't work, there are two problems. You don't want to know if every used tile matches your current one. You only want to know is some of them do. Then because those are the ones you want to remove, you need to negate this for filter. So you want something like !usedTiles.some(...).

    But there is another problem. You don't return anything in the callback to every/some:

        return usedTiles.every((el) => {
            _.isEqual(item, el)
        })
    

    You need to switch this to either

        return usedTiles.every((el) => _.isEqual(item, el))
    

    or

        return usedTiles.every((el) => {
            return _.isEqual(item, el)
        })
    

    It's an easy mistake to make, and it's quite common. If you're using an arrow function with a {-} delimited block, you need a return statement.