Menggunakan lodash untuk membandingkan array (keberadaan item tanpa urutan)

126

Saya tahu saya bisa melakukannya menggunakan loop, tetapi saya mencoba menemukan cara elegan untuk melakukan ini:

Saya memiliki dua array:

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

Saya ingin menggunakan lodashuntuk mengonfirmasi bahwa dua larik di atas adalah sama. Yang saya maksud dengan 'sama' adalah bahwa tidak ada item di array1dalamnya yang tidak terkandung di dalamnya array2.

Dalam hal memeriksa kesetaraan antara item-item ini:

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

atau

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

Keduanya bekerja karena huruf akan selalu teratur.

pQuestions123
sumber

Jawaban:

224

Jika Anda mengurutkan larik luar, Anda bisa menggunakan _.isEqual()karena larik dalam sudah diurutkan.

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

Perhatikan bahwa .sort()array akan dimutasi. Jika itu masalah bagi Anda, buat salinan terlebih dahulu menggunakan (misalnya) .slice()atau operator penyebaran ( ...).

Atau, lakukan seperti yang direkomendasikan Daniel Budick dalam komentar di bawah ini:

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

Lodash sortBy()tidak akan mengubah array.

Trott
sumber
6
Pertimbangkan bahwa array.sort () memutasi array asli. Mungkin yang ini mungkin lebih baik: var array1 = [['a', 'b'], ['b', 'c']]; var larik2 = [['b', 'c'], ['a', 'b']]; _.isEqual ([... larik1] .sort (), [... larik2] .sort ()); // benar
Yaniv Efraim
3
Menambahkan dua kalimat yang mencatat bahwa .sort()bermutasi dan menyarankan opsi untuk menyalin terlebih dahulu jika itu masalah bagi pengguna.
Trott
6
Jika sudah terlanjur menggunakan lodash, bisa saja Anda lakukan _.isEqual(_.sortBy(array1), _sortBy(array2))untuk mencegah mutasi.
Daniel Budick
1
@DanielBudick Terima kasih! Saya telah menambahkan itu ke jawabannya. Saran bagus.
Trott
33

Anda bisa menggunakan lodash xoruntuk ini

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

Jika Anda menganggap array [1, 1] berbeda dari array [1] maka Anda dapat meningkatkan kinerja sedikit seperti ini:

doArraysContainSameElements = arr1.length === arr2.length === 0 && _.xor(arr1, arr2).length === 0
Stephan Hoyer
sumber
1
Array perlu disortir.
Sovattha Sok
1
Ini seharusnya menjadi cara yang lebih baik untuk versi yang lebih baru
Leonardo
@Sovattha Sok Array tidak perlu diurutkan. Melakukan _.xor([3,4], [4,3]).length == 0akan membuat Anda menjadi kenyataan.
Nikolay
1
Perhatian: Teknik ini bekerja dengan baik untuk larik "kecil" tetapi ini bisa menjadi kinerja yang menyakitkan dan bijaksana jika larik Anda besar dan sebagian besar berbeda karena _.xor () akan terus berjalan melewati perbedaan pertama. Dengan kata lain itu tidak kembali cepat pada perbedaan pertama yang terdeteksi.
XDS
6

Yang saya maksud dengan 'sama' adalah tidak ada item dalam array1 yang tidak terkandung dalam array2.

Anda bisa menggunakan flatten () dan difference () untuk ini, yang berfungsi dengan baik jika Anda tidak peduli jika ada item array2yang tidak diarray1 . Kedengarannya seperti yang Anda tanyakan apakah array1 merupakan subset dari 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
Adam Boduch
sumber
4

Sudah ada jawaban di sini, tapi inilah implementasi JS murni saya. Saya tidak yakin apakah ini optimal, tetapi pasti transparan, dapat dibaca, dan sederhana.

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

Alasannya contains()adalah jika amemang mengandung semua elemen b, maka menempatkannya ke dalam himpunan yang sama tidak akan mengubah ukurannya.

Misalnya, jika const a = [1,2,3,4]dan const b = [1,2], makanew Set([...a, ...b]) === {1,2,3,4} . Seperti yang Anda lihat, set yang dihasilkan memiliki elemen yang sama dengan a.

Dari situ, agar lebih ringkas, kita bisa meringkasnya menjadi berikut ini:

const isEqualSet = (a, b) => {
  const unionSize = new Set([...a, ...b])
  return unionSize === a.length && unionSize === b.length
}
J.Ko
sumber
1

PURE JS (juga berfungsi jika array dan subarrays memiliki lebih dari 2 elemen dengan urutan arbitrer). Jika string berisi ,use as join('-')parametr character (can be utf) yang tidak digunakan dalam string

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

Kamil Kiełczewski
sumber
0

Kita bisa menggunakan _.differencefungsi untuk melihat apakah ada perbedaan atau tidak.

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

Saya harap ini akan membantu Anda.

Amitesh
sumber
5
Salah, fungsi Anda akan mendapatkan trueuntukconsole.log(isSame([1,2], [2,3,1]));
David Lin
3
Terima kasih @DavidLin untuk menunjukkannya. Saya telah membuat perubahan untuk mempertimbangkan kasus itu. Terima kasih dan maaf atas ketidaknyamanannya.
Amitesh
2
Anda tidak perlu berpindah tempat untuk a & b jika panjangnya tidak sama. Jika panjangnya berbeda maka mereka tidak bisa sama, jadi if (arrayOne.lenght! == arrayTwo.lenght) return false;
Alex
1
-1 Tidak ada gunanya memiliki itu adan bvariabel. Anda hanya menggunakan variabel tersebut dalam if-thenbagian, dan hal pertama yang Anda lakukan di sana adalah membuang nilai-nilai dimuat pada baris 2. Saya pikir satu baris berikut akan bekerja persis sama: return arrayOne.length <= arrayTwo.length && _.isEmpty(_.difference(arrayTwo.sort(), arrayTwo.sort());. Dan <=juga bisa ditingkatkan menjadi ===.
Mariano Desanze
1
Dan _.differenceakan mengembalikan item yang hilang dari argumen pertama, tetapi tidak ada item yang hilang pada argumen kedua. Jadi ini tidak benar akan kembali trueketika Anda mengulang item pada tanggal 1 di 2: isSame([1, 2, 3], [1, 2, 2]).
Mariano Desanze
0

Sunting: Saya melewatkan aspek multi-dimensi dari pertanyaan ini, jadi saya meninggalkan ini di sini jika itu membantu orang membandingkan array satu dimensi

Ini pertanyaan lama, tapi saya mengalami masalah dengan kecepatan penggunaan .sort()atau sortBy(), jadi saya menggunakan ini sebagai gantinya:

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

Itu dimaksudkan untuk gagal dengan cepat, dan untuk tujuan saya berfungsi dengan baik.

charliematters
sumber
Apakah pemeriksaan terakhir benar-benar diperlukan? array1.every((str) => array2.includes(str))Seharusnya cukup. Juga OP ingin menggunakan lodash, Anda setidaknya harus mengatakan mengapa Anda mengusulkan solusi vanillaJS (... sekarang kami memiliki semua dan termasuk ...). Berikan juga contoh bagaimana menerapkan fungsi Anda ke masalah yang disediakan (array 2 dimensi).
line-o
Itu poin yang bagus - saya tidak membahas aspek multi-dimensionalnya, bukan persyaratan untuk lodash. Saya pikir akan bermanfaat bagi siapa pun yang menelusuri (seperti yang saya lakukan) untuk lodash methods to compare arrays without considering ordermelihat alternatif dalam Javascript modern. Pemeriksaan kedua diperlukan karena arraysContainSameStrings(['1', '2', '2'], ['1', '2', '3'])akan mengembalikan nilai true jika tidak. Saya akan meninggalkannya di sini, karena mungkin membantu, tetapi saya menghargai saya belum menjawab pertanyaannya
charliematters