Использование lodash для сравнения массивов (наличие элементов без порядка)

125

Я знаю, что могу делать это с помощью циклов, но я пытаюсь найти элегантный способ сделать это:

У меня есть два массива:

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

Я хочу использовать, lodashчтобы подтвердить, что два вышеуказанных массива одинаковы. Под «таким же» я подразумеваю, что в нем нет элемента, array1который не содержится в array2.

Что касается проверки равенства между этими элементами:

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

или

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

Оба работают, так как буквы всегда будут в порядке.

pQuestions123
источник

Ответы:

224

Если вы отсортируете внешний массив, вы можете использовать, _.isEqual()поскольку внутренний массив уже отсортирован.

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

Обратите внимание, что это .sort()приведет к изменению массивов. Если для вас это проблема, сначала сделайте копию, используя (например) .slice()или оператор распространения ( ...).

Или сделайте так, как рекомендует Дэниел Будик в комментарии ниже:

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

Lodash не sortBy()будет изменять массив.

Trott
источник
6
Учтите, что array.sort () изменяет исходный массив. Может быть, этот вариант лучше: var array1 = [['a', 'b'], ['b', 'c']]; var array2 = [['b', 'c'], ['a', 'b']]; _.isEqual ([... array1] .sort (), [... array2] .sort ()); // true
Янив Эфраим
3
Добавлены два предложения, в которых отмечается, что они .sort()изменяются, и предлагаются варианты копирования, если это проблема для пользователя.
Trott
6
Если вы уже используете lodash, вы можете просто _.isEqual(_.sortBy(array1), _sortBy(array2))предотвратить мутацию.
Дэниел Будик
1
@DanielBudick Спасибо! Я добавил это к ответу. Отличное предложение.
Тротт
33

Вы можете использовать xorдля этого lodashs

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

Если вы считаете, что array [1, 1] отличается от array [1], вы можете улучшить производительность примерно так:

doArraysContainSameElements = arr1.length === arr2.length === 0 && _.xor(arr1, arr2).length === 0
Стефан Хойер
источник
1
Массивы все равно нужно отсортировать.
Sovattha Sok
1
Это должно быть лучший способ для новой версии
Леонардо
@Sovattha Sok Массивы не нужно сортировать. Делая, _.xor([3,4], [4,3]).length == 0вы получите правду.
Николай
1
Предупреждение: этот метод хорошо работает для «маленьких» массивов, но может быть проблематичным с точки зрения производительности и памяти, если ваши массивы огромны и в основном различаются, потому что _.xor () будет продолжать работать без первого различия. Другими словами, он не возвращается быстро при первом обнаруженном различии.
XDS
6

Под «тем же» я подразумеваю, что в массиве array1 нет элементов, не содержащихся в array2.

Вы можете использовать Flatten () и разности () для этого, который хорошо работает , если вы не волнует , если есть элементы , в array2которые не являются в array1. Похоже, вы спрашиваете, является ли array1 подмножеством 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
Адам Бодух
источник
4

Здесь уже есть ответы, но вот моя чистая реализация JS. Я не уверен, что это оптимально, но он уверен, что он прозрачен, удобочитаем и прост.

// Does array a contain elements of array b?
const contains = (a, b) => new Set([...a, ...b]).size === a.length
const isEqualSet = (a, b) => contains(a, b) && contains(b, a)

Обоснование в contains()том, что, если aон содержит все элементы b, то помещение их в один и тот же набор не изменит размер.

Например, если const a = [1,2,3,4]и const b = [1,2], то new Set([...a, ...b]) === {1,2,3,4}. Как видите, получившийся набор содержит те же элементы, что и a.

Оттуда, чтобы сделать его более кратким, мы можем свести его к следующему:

const isEqualSet = (a, b) => {
  const unionSize = new Set([...a, ...b])
  return unionSize === a.length && unionSize === b.length
}
J.Ko
источник
1

PURE JS (работает также, когда массивы и подмассивы содержат более 2 элементов в произвольном порядке). Если строки содержат символ ,use как join('-')параметр (может быть utf), который не используется в строках

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

Камил Келчевски
источник
0

Мы можем использовать _.differenceфункцию, чтобы увидеть, есть ли разница или нет.

function isSame(arrayOne, arrayTwo) {
   var a = _.unique(arrayOne),
       b = _.unique(arrayTwo);

   if (a.length <= b.length) {
      a = arrayTwo;
      b = arrayOne;
      return _.isEmpty(_.difference(a.sort(), b.sort()));
   } else {
      return false;
   }

}

// 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

Я надеюсь, что это поможет вам.

Amitesh
источник
5
Неправильно, ваша функция получит trueзаconsole.log(isSame([1,2], [2,3,1]));
Дэвид Линь
3
Спасибо @DavidLin, чтобы указать на это. Я внес изменения, чтобы рассмотреть этот случай. Благодарим вас и приносим извинения за неудобства.
Амитеш
2
вам не нужно менять местами a и b, если длины не совпадают. Если длины различаются, они уже не могут быть одинаковыми, поэтому if (arrayOne.lenght! == arrayTwo.lenght) return false;
Alex
1
-1 Нет смысла иметь те aи bпеременные. Вы только использовать эти переменные внутри if-thenчасти, и первое , что вы делаете там сбросить эти значения , погруженные в строке 2. Я думаю , следующие одна линия будет работать точно так же: return arrayOne.length <= arrayTwo.length && _.isEmpty(_.difference(arrayTwo.sort(), arrayTwo.sort());. И <=его также можно улучшить в ===.
Мариано Десанце 08
1
И _.differenceвернет недостающие элементы 1-го аргумента, но не пропущенные элементы 2-го. Так что это будет неправильно вернуться , trueкогда вы повторите пункты с 1 по 2: isSame([1, 2, 3], [1, 2, 2]).
Мариано Десанце
0

Изменить: я пропустил многомерный аспект этого вопроса, поэтому оставляю его здесь на случай, если он поможет людям сравнивать одномерные массивы

Это старый вопрос, но у меня были проблемы со скоростью использования .sort()или sortBy(), поэтому я использовал его вместо:

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

Он должен был быстро выйти из строя, и для моих целей работает нормально.

charliematters
источник
Действительно ли необходима последняя проверка? array1.every((str) => array2.includes(str))должно хватить. Также OP хотел использовать lodash, вы должны хотя бы сказать, почему вы предлагаете решение vanillaJS (... теперь, когда у нас есть все и включает ...). Также предоставьте пример того, как применить вашу функцию к указанной проблеме (двумерные массивы).
линейка
Это хороший момент - я не коснулся его многомерного аспекта, а не требования lodash. Я подумал, что было бы полезно, если бы кто-нибудь (как и я) искал lodash methods to compare arrays without considering orderальтернативу в современном Javascript. Вторая проверка необходима arraysContainSameStrings(['1', '2', '2'], ['1', '2', '3']), иначе вернет true. Я оставлю его здесь, так как это может помочь, но я понимаю, что не ответил на вопрос
charliematters