Використання 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'] 

Обидва працюють, оскільки букви завжди будуть в порядку.

Відповіді:


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

Лодаш не sortBy()буде мутувати масив.


6
Враховуйте, що array.sort () мутує вихідний масив. Можливо, це може бути краще: var array1 = [['a', 'b'], ['b', 'c']]; var array2 = [['b', 'c'], ['a', 'b']]; _.isEqual ([... array1] .sort (), [... array2] .sort ()); // правда
Янів Ефраїм

3
Додано два речення, зазначаючи, що він .sort()мутує і пропонує варіанти для копіювання спочатку, якщо це проблема для користувача.
Тротт

6
Якщо ви вже використовуєте лодаш, ви можете просто зробити, _.isEqual(_.sortBy(array1), _sortBy(array2))щоб запобігти мутації.
Даніель Будік

1
@DanielBudick Дякую! Я додав це до відповіді. Чудова пропозиція.
Тротт

33

Ви можете використовувати lodashs xorдля цього

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

Якщо ви вважаєте, що масив [1, 1] відрізняється від масиву [1], ви можете трохи покращити продуктивність так:

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

1
Масиви в будь-якому випадку потрібно сортувати.
Соватта Сок

1
Це має бути кращим способом для нової версії
Леонардо

@Sovattha Sok Масиви не потрібно сортувати. Якщо _.xor([3,4], [4,3]).length == 0ви зробите це, ви справдитеся.
Микола

1
Слово обережності: Ця методика добре працює для "малих" масивів, але вона може бути больовою і пам'ятною, якщо ваші масиви є величезними і в основному відрізняються, оскільки _.xor () буде продовжувати проходити повз першої різниці. Іншими словами, вона не повертається швидко при першій виявленій різниці.
XDS

6

Під "тим самим" я маю на увазі, що в масиві1 немає жодного елемента, який не міститься в масиві2.

Ви можете використовувати Flatten () і різниці () для цього, який добре працює , якщо ви не хвилює , якщо є елементи , в array2які не є в array1. Здається, ви запитуєте array1 як підмножину масиву2 ?

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
}

1

PURE JS (працює також тоді, коли масиви та підмасиви мають більше 2 елементів у довільному порядку). Якщо рядки містять ,використання в якості 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

Я сподіваюся, що це вам допоможе.


5
Неправильно, ваша функція отримає trueзаconsole.log(isSame([1,2], [2,3,1]));
Девід Лін

3
Дякую @DavidLin, щоб вказати на це. Я вніс зміни, щоб розглянути цю справу. Дякую і вибачте за незручності.
Амітеш

2
вам не потрібно перемикати місця для значень & b, якщо довжини не однакові. Якщо довжини відрізняються, то вони вже не можуть бути однаковими, тому якщо (arrayOne.lenght! == arrayTwo.lenght) повернути помилково;
Олексій

1
-1 Немає сенсу мати ці aта bзмінні. Ви тільки використовувати ці змінні всередині if-thenчастини, і перше , що ви робите там скинути ці значення , занурені в рядку 2. Я думаю , такі одна лінія буде працювати точно так же: return arrayOne.length <= arrayTwo.length && _.isEmpty(_.difference(arrayTwo.sort(), arrayTwo.sort());. А <=також можна вдосконалити ===.
Маріано Дезанзе

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))
  )
}

Це було призначено швидко вийти з ладу, і для моїх цілей прекрасно працює.


Чи справді потрібна остання перевірка? array1.every((str) => array2.includes(str))повинно вистачити Також ОП хотіла використати lodash, вам слід принаймні сказати, чому ви пропонуєте рішення vanillaJS (... тепер, коли ми маємо все і включає ...). Надайте також приклад, як застосувати свою функцію до наданої проблеми (двовимірні масиви).
лінія-о

Це хороший момент - я не звертався до багатовимірного аспекту цього, а не до вимоги лодаш. Я вважав, що корисно, щоб кожен, хто шукав (як я), lodash methods to compare arrays without considering orderпобачив альтернативу в сучасному Javascript. Друга перевірка необхідна, як arraysContainSameStrings(['1', '2', '2'], ['1', '2', '3'])інакше повернеться істина. Я залишу його тут, як це може допомогти, але я вдячний, що не відповів на питання
charliematters
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.