Using lodash to compare jagged arrays (items existence without order)

JavascriptArraysLodashJagged Arrays

Javascript Problem Overview


I know I can do it using loops, but I'm trying to find an elegant way of doing this:

I have two jagged arrays (array of arrays):

var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];

I want to use lodash to confirm that the above two jagged arrays are the same. By 'the same' I mean that there is no item in array1 that is not contained in array2. Notice that the items in jagged array are actually arrays. So I want to compare between inner arrays.

In terms of checking equality between these items:

['a', 'b'] == ['b', 'a'] 

or

['a', 'b'] == ['a', 'b'] 

Both work since the letters will always be in order.


UPDATE: Original question was talking about to "arrays" (instead of jagged arrays) and for years many people discussed (and added answers) about comparing simple one-dimensional arrays (without noticing that the examples provided in the question were not actually similar to the simple one-dimensional arrays they were expecting).

Javascript Solutions


Solution 1 - Javascript

If you sort the outer array, you can use _.isEqual() since the inner array is already sorted.

var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];
_.isEqual(array1.sort(), array2.sort()); //true

Note that .sort() will mutate the arrays. If that's a problem for you, make a copy first using (for example) .slice() or the spread operator (...).

Or, do as Daniel Budick recommends in a comment below:

_.isEqual(_.sortBy(array1), _.sortBy(array2))

Lodash's sortBy() will not mutate the array.

Solution 2 - Javascript

You can use lodashs xor for this

doArraysContainSameElements = _.xor(arr1, arr2).length === 0

If you consider array [1, 1] to be different than array [1] then you may improve performance a bit like so:

doArraysContainSameElements = arr1.length === arr2.length && _.xor(arr1, arr2).length === 0

Solution 3 - Javascript

There are already answers here, but here's my pure JS implementation. I'm not sure if it's optimal, but it sure is transparent, readable, and simple.

// Does array a contain elements of array b?
const union = new Set([...a, ...b]);
const contains = (a, b) => union.size === a.length && union.size === b.length;
// Since order is not important, just data validity.
const isEqualSet = (a, b) => union.contains(a, b) || union.contains(b, a)

The rationale in contains() is that if a does contain all the elements of b, then putting them into the same set would not change the size.

For example, if const a = [1,2,3,4] and const b = [1,2], then new Set([...a, ...b]) === {1,2,3,4}. As you can see, the resulting set has the same elements as a.

From there, to make it more concise, we can boil it down to the following:

const isEqualSet = (a: string[], b: sting[]): boolean => {
  const union = new Set([...a, ...b])
  return union.size === a.length && union.size === b.length;
}

Edit: This will not work with obj[{a: true}, true, 3] but does compare array contents probably as long as they are primitive elements. Method fixed and tested against strings two arrays using the same values in different orders. Does not work with object types. I recommend making a universal helper which calls a helper function depending on the type which needs to be compared. Try _.isEqual(a. b); from the very fantastic lodash library.

Solution 4 - Javascript

> By 'the same' I mean that there are is no item in array1 that is not contained in array2.

You could use flatten() and difference() for this, which works well if you don't care if there are items in array2 that aren't in array1. It sounds like you're asking is array1 a subset of array2?

var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];

function isSubset(source, target) {
    return !_.difference(_.flatten(source), _.flatten(target)).length;
}

isSubset(array1, array2); // → true
array1.push('d');
isSubset(array1, array2); // → false
isSubset(array2, array1); // → true

Solution 5 - Javascript

PURE JS (works also when arrays and subarrays has more than 2 elements with arbitrary order). If strings contains , use as join('-') parametr character (can be utf) which is not used in strings

array1.map(x=>x.sort()).sort().join() === array2.map(x=>x.sort()).sort().join()

var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['b', 'a']];

var r = array1.map(x=>x.sort()).sort().join() === array2.map(x=>x.sort()).sort().join();

console.log(r);

Solution 6 - Javascript

I definitely feel very unclean for posting this solution, but:

var array1 = [['a', 'b'], ['b', 'c']];
var array2 = [['b', 'c'], ['a', 'b']];
_.isMatch([array1], [array2]) && _.isMatch([array2], [array1]) // true

array1 = [['b', 'a'], ['c', 'b']];
array2 = [['b', 'c'], ['a', 'b']];
_.isMatch([array1], [array2]) && _.isMatch([array2], [array1]) // also true

Note that you have to wrap array1 and array2 into a container (array, object) in order for this to work? Why? There's probably a perfectly stupid reason for this.

Solution 7 - Javascript

import { differenceBy } from 'lodash'

export default function (arr1, arr2) {
    return !differenceBy(arr1, arr2).length && arr1.length === arr2.length
}

if there are no different characters and the array length is the same, it makes them the same.

Solution 8 - Javascript

Edit: I missed the multi-dimensional aspect of this question, so I'm leaving this here in case it helps people compare one-dimensional arrays

It's an old question, but I was having issues with the speed of using .sort() or sortBy(), so I used this instead:

function arraysContainSameStrings(array1: string[], array2: string[]): boolean {
  return (
    array1.length === array2.length &&
    array1.every((str) => array2.includes(str)) &&
    array2.every((str) => array1.includes(str))
  )
}

It was intended to fail fast, and for my purposes works fine.

Solution 9 - Javascript

We can use _.difference function to see if there is any difference or not.

function isSame(arrayOne, arrayTwo) {
   var a = _.uniq(arrayOne),
   b = _.uniq(arrayTwo);
   return a.length === b.length && 
          _.isEmpty(_.difference(b.sort(), a.sort()));
}

// examples
console.log(isSame([1, 2, 3], [1, 2, 3])); // true
console.log(isSame([1, 2, 4], [1, 2, 3])); // false
console.log(isSame([1, 2], [2, 3, 1])); // false
console.log(isSame([2, 3, 1], [1, 2])); // false

// Test cases pointed by Mariano Desanze, Thanks.
console.log(isSame([1, 2, 3], [1, 2, 2])); // false
console.log(isSame([1, 2, 2], [1, 2, 2])); // true
console.log(isSame([1, 2, 2], [1, 2, 3])); // false

I hope this will help you.

Adding example link at StackBlitz

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionpQuestions123View Question on Stackoverflow
Solution 1 - JavascriptTrottView Answer on Stackoverflow
Solution 2 - JavascriptStephan HoyerView Answer on Stackoverflow
Solution 3 - JavascriptJ.KoView Answer on Stackoverflow
Solution 4 - JavascriptAdam BoduchView Answer on Stackoverflow
Solution 5 - JavascriptKamil KiełczewskiView Answer on Stackoverflow
Solution 6 - JavascriptDejay ClaytonView Answer on Stackoverflow
Solution 7 - JavascriptM. Yagiz YazicilarView Answer on Stackoverflow
Solution 8 - JavascriptcharliemattersView Answer on Stackoverflow
Solution 9 - JavascriptAmiteshView Answer on Stackoverflow