Как сравнить массивы в JavaScript?
Я хотел бы сравнить два массива... в идеале, эффективно. Ничего особенного, просто true
если они идентичны, и false
если не. Не удивительно, что оператор сравнения не работает.
var a1 = [1,2,3];
var a2 = [1,2,3];
console.log(a1==a2); // Returns false
console.log(JSON.stringify(a1)==JSON.stringify(a2)); // Returns true
JSON кодирует каждый массив, но есть ли более быстрый или "лучший" способ простого сравнения массивов без необходимости перебирать каждое значение?
84 ответа
Чтобы сравнить массивы, просмотрите их и сравните каждое значение:
Сравнение массивов:
// Warn if overriding existing method
if(Array.prototype.equals)
console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
// Hide method from for-in loops
Object.defineProperty(Array.prototype, "equals", {enumerable: false});
Использование:
[1, 2, [3, 4]].equals([1, 2, [3, 2]]) === false;
[1, "2,3"].equals([1, 2, 3]) === false;
[1, 2, [3, 4]].equals([1, 2, [3, 4]]) === true;
[1, 2, 1, 2].equals([1, 2, 1, 2]) === true;
Вы можете сказать: " Но сравнивать строки гораздо быстрее - никаких циклов... ", тогда вы должны заметить, что есть циклы ARE. Первый рекурсивный цикл, который преобразует массив в строку, а второй, который сравнивает две строки. Так что этот метод быстрее, чем использование строки.
Я считаю, что большие объемы данных всегда должны храниться в массивах, а не в объектах. Однако, если вы используете объекты, их тоже можно частично сравнить.
Вот как:
Сравнение объектов:
Я уже говорил выше, что два экземпляра объекта никогда не будут равны, даже если они на данный момент содержат одинаковые данные:
({a:1, foo:"bar", numberOfTheBeast: 666}) == ({a:1, foo:"bar", numberOfTheBeast: 666}) //false
Это имеет причину, поскольку, например, внутри объектов могут быть частные переменные.
Однако, если вы просто используете структуру объекта для хранения данных, сравнение все равно возможно:
Object.prototype.equals = function(object2) {
//For the first loop, we only check for types
for (propName in this) {
//Check for inherited methods and properties - like .equals itself
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
//Return false if the return value is different
if (this.hasOwnProperty(propName) != object2.hasOwnProperty(propName)) {
return false;
}
//Check instance type
else if (typeof this[propName] != typeof object2[propName]) {
//Different types => not equal
return false;
}
}
//Now a deeper check using other objects property names
for(propName in object2) {
//We must check instances anyway, there may be a property that only exists in object2
//I wonder, if remembering the checked values from the first loop would be faster or not
if (this.hasOwnProperty(propName) != object2.hasOwnProperty(propName)) {
return false;
}
else if (typeof this[propName] != typeof object2[propName]) {
return false;
}
//If the property is inherited, do not check any more (it must be equa if both objects inherit it)
if(!this.hasOwnProperty(propName))
continue;
//Now the detail check and recursion
//This returns the script back to the array comparing
/**REQUIRES Array.equals**/
if (this[propName] instanceof Array && object2[propName] instanceof Array) {
// recurse into the nested arrays
if (!this[propName].equals(object2[propName]))
return false;
}
else if (this[propName] instanceof Object && object2[propName] instanceof Object) {
// recurse into another objects
//console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
if (!this[propName].equals(object2[propName]))
return false;
}
//Normal value comparison for strings and numbers
else if(this[propName] != object2[propName]) {
return false;
}
}
//If everything passed, let's say YES
return true;
}
Однако помните, что этот служит для сравнения данных, подобных JSON, а не экземпляров классов и прочего. Если вы хотите сравнить более сложные объекты, посмотрите на этот ответ, и это сверхдлинная функция.
Чтобы сделать эту работу с Array.equals
Вы должны немного отредактировать оригинальную функцию:
...
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
/**REQUIRES OBJECT COMPARE**/
else if (this[i] instanceof Object && array[i] instanceof Object) {
// recurse into another objects
//console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
...
Я сделал небольшой тестовый инструмент для обеих функций.
Бонус: вложенные массивы с indexOf
а также contains
Сэми Бенчериф подготовил полезные функции для случая, когда вы ищете конкретный объект во вложенных массивах, которые доступны здесь: https://jsfiddle.net/SamyBencherif/8352y6yw/
Хотя это работает только для скалярных массивов (см. Примечание ниже), оно короткое:
array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})
Rr, в ECMAScript 6 / CoffeeScript / TypeScript с функциями стрелки:
array1.length === array2.length && array1.every((value, index) => value === array2[index])
(Примечание: здесь "скаляр" означает значения, которые можно сравнивать напрямую, используя ===
, Итак: числа, строки, объекты по ссылке, функции по ссылке. См. Ссылку MDN для получения дополнительной информации об операторах сравнения).
ОБНОВИТЬ
Из того, что я прочитал из комментариев, сортировка массива и сравнение может дать точный результат:
array1.length === array2.length && array1.sort().every(function(value, index) { return value === array2.sort()[index]});
Например:
array1 = [2,3,1,4];
array2 = [1,2,3,4];
Тогда приведенный выше код даст true
Мне нравится использовать библиотеку Underscore для проектов тяжелого кодирования массивов / объектов... в Underscore и Lodash, сравниваете ли вы массивы или объекты, это просто выглядит так:
_.isEqual(array1, array2) // returns a boolean
_.isEqual(object1, object2) // returns a boolean
Я думаю, что это самый простой способ сделать это с помощью JSON stringify, и в некоторых ситуациях это может быть лучшим решением:
JSON.stringify(a1) === JSON.stringify(a2);
Это преобразует объекты a1
а также a2
в строки, чтобы их можно было сравнить. Порядок важен в большинстве случаев, поскольку для этого можно отсортировать объект с помощью алгоритма сортировки, показанного в одном из приведенных выше ответов.
Обратите внимание, что вы больше не сравниваете объект, а представляете его как строковое представление. Это может быть не совсем то, что вы хотите.
В духе оригинального вопроса:
Я хотел бы сравнить два массива... в идеале, эффективно. Ничего особенного, просто правда, если они идентичны, и ложь, если нет.
Я проводил тесты производительности на некоторых из более простых предложений, предложенных здесь, со следующими результатами (от быстрого до медленного):
в то время как (67%) Тимом Дауном
var i = a1.length;
while (i--) {
if (a1[i] !== a2[i]) return false;
}
return true
каждый (69%) от пользователя2782196
a1.every((v,i)=> v === a2[i]);
уменьшить (74%) по DEI
a1.reduce((a, b) => a && a2.includes(b), true);
присоединиться к группе (78%) от Gaizka Allende & vivek
a1.join('') === a2.join('');
a1.toString() === a2.toString();
половина строки (90%) Виктора Паломо
a1 == a2.toString();
stringify (100%) от radtek
JSON.stringify(a1) === JSON.stringify(a2);
Обратите внимание, что в приведенных ниже примерах предполагается, что массивы - это отсортированные одномерные массивы.
.length
сравнение было удалено для общего теста (добавитьa1.length === a2.length
к любому из предложений, и вы получите повышение производительности ~10%). Выберите любые решения, которые лучше всего подходят для вас, зная скорость и ограничения каждого из них.Несвязанное примечание: интересно видеть людей, получающих всех довольных Джоном Уэйнсом нажатием кнопки "вниз" на совершенно законных ответах на этот вопрос.
Практический путь
Я думаю, что неправильно говорить, что конкретная реализация "Правильный путь ™", если она только "правильная" ("правильная") в отличие от "неправильного" решения. Решение Томаша представляет собой явное улучшение по сравнению со сравнением массивов на основе строк, но это не означает, что оно объективно "правильно". Что правильно в любом случае? Это самый быстрый? Это самый гибкий? Это легче всего понять? Это самый быстрый для отладки? Использует ли он наименьшее количество операций? Есть ли у него побочные эффекты? Ни одно решение не может быть лучшим из всех.
Томаш мог бы сказать, что его решение быстрое, но я бы сказал, что оно слишком сложное. Он пытается быть универсальным решением, которое работает для всех массивов, вложенных или нет. Фактически, он даже принимает в качестве входных данных больше, чем просто массивы, и все еще пытается дать "правильный" ответ.
Дженерики предлагают повторное использование
Мой ответ подойдет к проблеме по-другому. Я начну с общего arrayCompare
Процедура, которая касается только шаг через массивы. Оттуда мы создадим другие наши основные функции сравнения, такие как arrayEqual
а также arrayDeepEqual
, так далее
// arrayCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayCompare = f => ([x,...xs]) => ([y,...ys]) =>
x === undefined && y === undefined
? true
: Boolean (f (x) (y)) && arrayCompare (f) (xs) (ys)
На мой взгляд, лучший вид кода даже не нуждается в комментариях, и это не исключение. Здесь так мало происходит, что вы можете понять поведение этой процедуры практически без усилий. Конечно, некоторые из синтаксиса ES6 могут показаться вам чуждыми, но это только потому, что ES6 является относительно новым.
Как подсказывает тип, arrayCompare
берет функцию сравнения, f
и два входных массива, xs
а также ys
, По большей части все, что мы делаем, это звоним f (x) (y)
для каждого элемента во входных массивах. Мы возвращаем рано false
если пользовательский f
возвращается false
- благодаря &&
Оценка короткого замыкания. Так что да, это означает, что компаратор может прекратить итерацию на ранней стадии и предотвратить циклическое прохождение через остальную часть входного массива, когда это не нужно.
Строгое сравнение
Далее, используя наш arrayCompare
функция, мы можем легко создавать другие функции, которые нам могут понадобиться. Начнем с простейшего arrayEqual
...
// equal :: a -> a -> Bool
const equal = x => y =>
x === y // notice: triple equal
// arrayEqual :: [a] -> [a] -> Bool
const arrayEqual =
arrayCompare (equal)
const xs = [1,2,3]
const ys = [1,2,3]
console.log (arrayEqual (xs) (ys)) //=> true
// (1 === 1) && (2 === 2) && (3 === 3) //=> true
const zs = ['1','2','3']
console.log (arrayEqual (xs) (zs)) //=> false
// (1 === '1') //=> false
Просто как тот. arrayEqual
можно определить с помощью arrayCompare
и функция сравнения, которая сравнивает a
в b
с помощью ===
(для строгого равенства).
Обратите внимание, что мы также определяем equal
как это собственная функция. Это подчеркивает роль arrayCompare
в качестве функции более высокого порядка, чтобы использовать наш компаратор первого порядка в контексте другого типа данных (Array).
Слабое сравнение
Мы могли бы так же легко определить arrayLooseEqual
используя ==
вместо. Теперь при сравнении 1
(Номер) в '1'
(Строка), результат будет true
...
// looseEqual :: a -> a -> Bool
const looseEqual = x => y =>
x == y // notice: double equal
// arrayLooseEqual :: [a] -> [a] -> Bool
const arrayLooseEqual =
arrayCompare (looseEqual)
const xs = [1,2,3]
const ys = ['1','2','3']
console.log (arrayLooseEqual (xs) (ys)) //=> true
// (1 == '1') && (2 == '2') && (3 == '3') //=> true
Глубокое сравнение (рекурсивное)
Вы, наверное, заметили, что это только поверхностное сравнение. Конечно, решение Томаша - "Правильный путь ™", потому что оно подразумевает глубокое сравнение, верно?
Ну наш arrayCompare
Процедура достаточно универсальна, чтобы использовать ее таким образом, чтобы сделать тест на глубокое равенство быстрым…
// isArray :: a -> Bool
const isArray =
Array.isArray
// arrayDeepCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayDeepCompare = f =>
arrayCompare (a => b =>
isArray (a) && isArray (b)
? arrayDeepCompare (f) (a) (b)
: f (a) (b))
const xs = [1,[2,[3]]]
const ys = [1,[2,['3']]]
console.log (arrayDeepCompare (equal) (xs) (ys)) //=> false
// (1 === 1) && (2 === 2) && (3 === '3') //=> false
console.log (arrayDeepCompare (looseEqual) (xs) (ys)) //=> true
// (1 == 1) && (2 == 2) && (3 == '3') //=> true
Просто как тот. Мы строим глубокий компаратор, используя другую функцию более высокого порядка. На этот раз мы завернем arrayCompare
используя собственный компаратор, который проверит, a
а также b
являются массивами. Если это так, повторно arrayDeepCompare
иначе сравнить a
а также b
указанному пользователем компаратору (f
). Это позволяет нам отделить поведение глубокого сравнения от того, как мы на самом деле сравниваем отдельные элементы. То есть, как показано в примере выше, мы можем глубоко сравнить, используя equal
, looseEqual
или любой другой компаратор, который мы делаем.
Так как arrayDeepCompare
карри, мы можем частично применить это так же, как и в предыдущих примерах
// arrayDeepEqual :: [a] -> [a] -> Bool
const arrayDeepEqual =
arrayDeepCompare (equal)
// arrayDeepLooseEqual :: [a] -> [a] -> Bool
const arrayDeepLooseEqual =
arrayDeepCompare (looseEqual)
Для меня это уже явное улучшение по сравнению с решением Томаша, потому что я могу явно выбирать поверхностное или глубокое сравнение для своих массивов, по мере необходимости.
Сравнение объектов (пример)
А что если у вас есть массив объектов или что-то еще? Может быть, вы хотите считать эти массивы "равными", если каждый объект имеет одинаковый id
значение …
// idEqual :: {id: Number} -> {id: Number} -> Bool
const idEqual = x => y =>
x.id !== undefined && x.id === y.id
// arrayIdEqual :: [a] -> [a] -> Bool
const arrayIdEqual =
arrayCompare (idEqual)
const xs = [{id:1}, {id:2}]
const ys = [{id:1}, {id:2}]
console.log (arrayIdEqual (xs) (ys)) //=> true
// (1 === 1) && (2 === 2) //=> true
const zs = [{id:1}, {id:6}]
console.log (arrayIdEqual (xs) (zs)) //=> false
// (1 === 1) && (2 === 6) //=> false
Просто как тот. Здесь я использовал ванильные объекты JS, но этот тип компаратора может работать для любого типа объекта; даже ваши пользовательские объекты. Решение Томаша должно быть полностью переработано для поддержки такого теста на равенство
Глубокий массив с объектами? Не проблема. Мы создали универсальные универсальные функции, которые будут работать в самых разных случаях.
const xs = [{id:1}, [{id:2}]]
const ys = [{id:1}, [{id:2}]]
console.log (arrayCompare (idEqual) (xs) (ys)) //=> false
console.log (arrayDeepCompare (idEqual) (xs) (ys)) //=> true
Произвольное сравнение (пример)
Или что, если вы хотите провести какое-то совершенно произвольное сравнение? Может быть, я хочу знать, если каждый x
больше чем каждый y
...
// gt :: Number -> Number -> Bool
const gt = x => y =>
x > y
// arrayGt :: [a] -> [a] -> Bool
const arrayGt = arrayCompare (gt)
const xs = [5,10,20]
const ys = [2,4,8]
console.log (arrayGt (xs) (ys)) //=> true
// (5 > 2) && (10 > 4) && (20 > 8) //=> true
const zs = [6,12,24]
console.log (arrayGt (xs) (zs)) //=> false
// (5 > 6) //=> false
Меньше - больше
Вы можете видеть, что мы на самом деле делаем больше с меньшим количеством кода. Там нет ничего сложного arrayCompare
Сам и каждый из созданных нами пользовательских компараторов имеет очень простую реализацию.
С легкостью мы можем точно определить, как мы хотим, чтобы сравнивались два массива - мелкий, глубокий, строгий, свободный, какое-либо свойство объекта или какое-либо произвольное вычисление, или любая их комбинация - все с использованием одной процедуры, arrayCompare
, Может быть, даже мечтать RegExp
компаратор! Я знаю, как дети любят эти регулярные выражения...
Это самый быстрый? Нету. Но это, вероятно, не должно быть либо. Если бы скорость была единственной метрикой, используемой для измерения качества нашего кода, много действительно хорошего кода было бы выброшено - вот почему я называю этот подход Практическим путем. Или, может быть, чтобы быть более справедливым, Практический путь. Это описание подходит для этого ответа, потому что я не говорю, что этот ответ является только практическим по сравнению с некоторым другим ответом; это объективно верно. Мы достигли высокой степени практичности с очень небольшим количеством кода, который очень легко рассуждать. Ни один другой код не может сказать, что мы не заработали это описание.
Это делает это "правильным" решением для вас? Вам решать. И никто другой не может сделать это для вас; только вы знаете, каковы ваши потребности. Почти во всех случаях я ценю простой, практичный и универсальный код перед умным и быстрым. То, что вы цените, может отличаться, поэтому выберите то, что вам подходит.
редактировать
Мой старый ответ был больше сосредоточен на разложении arrayEqual
в крошечные процедуры. Это интересное упражнение, но не самый лучший (самый практичный) способ решения этой проблемы. Если вам интересно, вы можете увидеть эту историю изменений.
Непонятно, что вы подразумеваете под "тождественным". Например, являются ли массивы a
а также b
ниже идентичных (обратите внимание на вложенные массивы)?
var a = ["foo", ["bar"]], b = ["foo", ["bar"]];
Вот оптимизированная функция сравнения массивов, которая сравнивает соответствующие элементы каждого массива по очереди, используя строгое равенство, и не выполняет рекурсивное сравнение элементов массива, которые сами являются массивами, что означает, что для приведенного выше примера arraysIdentical(a, b)
вернется false
, Это работает в общем случае, который JSON- и join()
решения не будут:
function arraysIdentical(a, b) {
var i = a.length;
if (i != b.length) return false;
while (i--) {
if (a[i] !== b[i]) return false;
}
return true;
};
Опираясь на ответ Томаша Зато, я согласен, что итерация по массивам - самая быстрая. Кроме того (как уже говорили другие), функцию следует называть равной / равной, а не сравнивать. В свете этого я изменил функцию для обработки сравнения массивов на предмет сходства - т.е. они имеют одинаковые элементы, но не по порядку - для личного использования, и подумал, что я добавлю это здесь для всеобщего обозрения.
Array.prototype.equals = function (array, strict) {
if (!array)
return false;
if (arguments.length == 1)
strict = true;
if (this.length != array.length)
return false;
for (var i = 0; i < this.length; i++) {
if (this[i] instanceof Array && array[i] instanceof Array) {
if (!this[i].equals(array[i], strict))
return false;
}
else if (strict && this[i] != array[i]) {
return false;
}
else if (!strict) {
return this.sort().equals(array.sort(), true);
}
}
return true;
}
Эта функция принимает дополнительный параметр строгого, который по умолчанию имеет значение true. Этот строгий параметр определяет, должны ли массивы быть полностью одинаковыми как по содержанию, так и по порядку этого содержимого, или просто содержать одно и то же содержимое.
Пример:
var arr1 = [1, 2, 3, 4];
var arr2 = [2, 1, 4, 3]; // Loosely equal to 1
var arr3 = [2, 2, 3, 4]; // Not equal to 1
var arr4 = [1, 2, 3, 4]; // Strictly equal to 1
arr1.equals(arr2); // false
arr1.equals(arr2, false); // true
arr1.equals(arr3); // false
arr1.equals(arr3, false); // false
arr1.equals(arr4); // true
arr1.equals(arr4, false); // true
Я также написал быстрый jsfiddle с помощью функции и этого примера:
http://jsfiddle.net/Roundaround/DLkxX/
В тех же строках, что и в JSON.encode, используется join().
function checkArrays( arrA, arrB ){
//check if lengths are different
if(arrA.length !== arrB.length) return false;
//slice so we do not effect the original
//sort makes sure they are in order
//join makes it a string so we can do a string compare
var cA = arrA.slice().sort().join(",");
var cB = arrB.slice().sort().join(",");
return cA===cB;
}
var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"]; //will return true
console.log( checkArrays(a,b) ); //true
console.log( checkArrays(a,c) ); //false
console.log( checkArrays(a,d) ); //false
console.log( checkArrays(a,e) ); //true
Единственная проблема заключается в том, что вы заботитесь о типах, которые тестирует последнее сравнение. Если вы заботитесь о типах, вам придется зацикливаться.
function checkArrays( arrA, arrB ){
//check if lengths are different
if(arrA.length !== arrB.length) return false;
//slice so we do not effect the orginal
//sort makes sure they are in order
var cA = arrA.slice().sort();
var cB = arrB.slice().sort();
for(var i=0;i<cA.length;i++){
if(cA[i]!==cB[i]) return false;
}
return true;
}
var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];
console.log( checkArrays(a,b) ); //true
console.log( checkArrays(a,c) ); //false
console.log( checkArrays(a,d) ); //false
console.log( checkArrays(a,e) ); //false
Если порядок должен оставаться прежним, чем просто цикл, сортировка не требуется.
function checkArrays( arrA, arrB ){
//check if lengths are different
if(arrA.length !== arrB.length) return false;
for(var i=0;i<arrA.length;i++){
if(arrA[i]!==arrB[i]) return false;
}
return true;
}
var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];
console.log( checkArrays(a,a) ); //true
console.log( checkArrays(a,b) ); //false
console.log( checkArrays(a,c) ); //false
console.log( checkArrays(a,d) ); //false
console.log( checkArrays(a,e) ); //false
Несмотря на то, что у этого есть много ответов, я полагаю, что это поможет:
const newArray = [ ...new Set( [...arr1, ...arr2] ) ]
В вопросе о том, как будет выглядеть структура массива, не указано, поэтому, если вы точно знаете, что в вашем массиве не будет ни вложенных массивов, ни объектов (это случилось со мной, поэтому я пришел к этому ответьте) приведенный выше код будет работать.
В результате мы используем оператор распространения ( ...) для объединения обоих массивов, а затем используем Set для устранения любых дубликатов. Если у вас есть это, вы можете сравнить их размеры, если все три массива имеют одинаковый размер, вы можете пойти.
Этот ответ также игнорирует порядок элементов, как я уже говорил, точная ситуация произошла со мной, так что, возможно, кто-то в такой же ситуации может оказаться здесь (как я).
Edit1.
Отвечая на вопрос Дмитрия Гринько: "Почему вы использовали оператор распространения ( ...) здесь - ... новый сет? Он не работает"
Рассмотрим этот код:
const arr1 = [ 'a', 'b' ]
const arr2 = [ 'a', 'b', 'c' ]
const newArray = [ new Set( [...arr1, ...arr2] ) ]
console.log(newArray)
Ты получишь
[ Set { 'a', 'b', 'c' } ]
Чтобы работать с этим значением, вам нужно использовать некоторые свойства Set (см. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set). С другой стороны, когда вы используете этот код:
const arr1 = [ 'a', 'b' ]
const arr2 = [ 'a', 'b', 'c' ]
const newArray = [ ...new Set( [...arr1, ...arr2] ) ]
console.log(newArray)
Ты получишь
[ 'a', 'b', 'c' ]
В этом-то и разница: первый дал бы мне набор, он бы тоже работал, так как я мог бы получить размер этого набора, но второй дает мне нужный мне массив, что более непосредственно связано с разрешением.
В моем случае сравниваемые массивы содержат только цифры и строки. Эта функция покажет вам, если массивы содержат одинаковые элементы.
function are_arrs_match(arr1, arr2){
return arr1.sort().toString() === arr2.sort().toString()
}
Давайте проверим это!
arr1 = [1, 2, 3, 'nik']
arr2 = ['nik', 3, 1, 2]
arr3 = [1, 2, 5]
console.log (are_arrs_match(arr1, arr2)) //true
console.log (are_arrs_match(arr1, arr3)) //false
Для массива чисел попробуйте:
a1==''+a2
var a1 = [1,2,3];
var a2 = [1,2,3];
console.log( a1==''+a2 )
Примечание: этот метод не будет работать, если массив также содержит строки, напримерa2 = [1, "2,3"]
.
Вы можете просто использовать isEqual из библиотеки lodash. Он очень эффективный и чистый.
import {isEqual} from "lodash";
const isTwoArraysEqual = isEqual(array1, array2);
Здесь много сложных длинных ответов, поэтому я просто хочу дать один очень простой ответ: используйте toString(), чтобы превратить массив в простую строку, разделенную запятыми, которую вы можете легко сравнить с ===
let a = [1, 2, 3]
let b = [1, 2, 3]
let c = [4, 2, 3]
console.log(a.toString()) // this outputs "1,2,3"
console.log(a.toString() === b.toString()) // this outputs true because "1,2,3" === "1,2,3"
console.log(a.toString() === c.toString()) // this outputs false because "1,2,3" != "4,2,3"
Кодекс игры в гольф
Есть множество ответов, показывающих, как эффективно сравнивать массивы.
Ниже приведен кратчайший способ сравнения двух массивов строк или int, измеренный в байтах кода.
Это работает, потому что при использовании
+
Оператор, типы автоматически преобразуются, чтобы разрешить конкатенацию. В этом случае
1
и
[1, 2, 3]
оба преобразуются в строку.
Внутри JavaScript использует
[1, 2, 3].join()
чтобы преобразовать массив в строку, а затем добавляет их, в результате чего
11,2,3
. Делая это на обоих массивах, можно просто использовать
===
или же
==
для сравнения двух строк.
ПРИМЕЧАНИЕ. Хотя это полезно для игры в гольф, его, вероятно, не следует использовать в производственном коде.
Ваш код не будет правильно обрабатывать случай, когда оба массива имеют одинаковые элементы, но не в одном и том же порядке.
Посмотрите на мой код с вашим примером, в котором сравниваются два массива, элементы которых являются числами, вы можете изменить или расширить его для других типов элементов (используя.join() вместо.toString()).
var a1 = [1,2,3];
var a2 = [1,2,3];
const arraysAreEqual = a1.sort().toString()==a2.sort().toString();
// true if both arrays have same elements else false
console.log(arraysAreEqual);
Здесь много хороших ответов. Вот как я обычно это делаю -
if ( arr1.length === arr2.length && arr1.every((a1) => arr2.includes(a1)) ) {
// logic
}
every()
будет возвращать true только в том случае, если все элементы проходят заданную логику сопоставления. Если он встречает ложь, в любой итерации он завершается и возвращает ложь.Временная сложность будет O(n*m).
В 2020 году предлагается этап 1, который позволяет легко сравнивать массивы путем добавленияArray.prototype.equals
к языку. Вот как это будет работать без каких-либо библиотек, обезьяньих исправлений или любого другого кода:
[1, 2, 3].equals([1, 2, 3]) // evaluates to true
[1, 2, undefined].equals([1, 2, 3]) // evaluates to false
[1, [2, [3, 4]]].equals([1, [2, [3, 4]]]) // evaluates to true
Пока это только предварительное предложение - TC39 теперь "посвятит время изучению проблемного пространства, решений и сквозных проблем". Если он дойдет до стадии 2, у него есть хорошие шансы в конечном итоге интегрироваться в сам язык.
Вот версия Typescript:
//https://stackru.com/a/16436975/2589276
export function arraysEqual<T>(a: Array<T>, b: Array<T>): boolean {
if (a === b) return true
if (a == null || b == null) return false
if (a.length != b.length) return false
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false
}
return true
}
//https://stackru.com/a/16436975/2589276
export function arraysDeepEqual<T>(a: Array<T>, b: Array<T>): boolean {
return JSON.stringify(a) === JSON.stringify(b)
}
Несколько тестов для мокко:
it('arraysEqual', function () {
let a = [1,2]
let b = [1,2]
let c = [2,3]
let d = [2, 3]
let e = ['car','apple','banana']
let f = ['car','apple','banana']
let g = ['car','apple','banan8']
expect(arraysEqual(a, b)).to.equal(true)
expect(arraysEqual(c, d)).to.equal(true)
expect(arraysEqual(a, d)).to.equal(false)
expect(arraysEqual(e, f)).to.equal(true)
expect(arraysEqual(f, g)).to.equal(false)
})
it('arraysDeepEqual', function () {
let a = [1,2]
let b = [1,2]
let c = [2,3]
let d = [2, 3]
let e = ['car','apple','banana']
let f = ['car','apple','banana']
let g = ['car','apple','banan8']
let h = [[1,2],'apple','banan8']
let i = [[1,2],'apple','banan8']
let j = [[1,3],'apple','banan8']
expect(arraysDeepEqual(a, b)).to.equal(true)
expect(arraysDeepEqual(c, d)).to.equal(true)
expect(arraysDeepEqual(a, d)).to.equal(false)
expect(arraysDeepEqual(e, f)).to.equal(true)
expect(arraysDeepEqual(f, g)).to.equal(false)
expect(arraysDeepEqual(h, i)).to.equal(true)
expect(arraysDeepEqual(h, j)).to.equal(false)
})
Если вы используете среду тестирования, такую как Mocha, с библиотекой утверждений Chai, вы можете использовать глубокое равенство для сравнения массивов.
expect(a1).to.deep.equal(a2)
Это должно возвращать истину, только если массивы имеют равные элементы с соответствующими индексами.
Если это только два массива чисел или строк, это быстрый однострочный
const array1 = [1, 2, 3];
const array2 = [1, 3, 4];
console.log(array1.join(',') === array2.join(',')) //false
const array3 = [1, 2, 3];
const array4 = [1, 2, 3];
console.log(array3.join(',') === array4.join(',')) //true
Для одноразмерного массива вы можете просто использовать:
arr1.sort().toString() == arr2.sort().toString()
это также позаботится о массиве с несовпадающим индексом.
Другой подход с очень небольшим количеством кода (с использованием Array Reduce и Array включает):
arr1.length == arr2.length && arr1.reduce((a, b) => a && arr2.includes(b), true)
Если вы хотите сравнить также равенство порядка:
arr1.length == arr2.length && arr1.reduce((a, b, i) => a && arr2[i], true)
length
check гарантирует, что набор элементов в одном массиве не является просто подмножеством другого.Редуктор используется для обхода одного массива и поиска каждого элемента в другом массиве. Если один элемент не найден, функция уменьшения возвращается
false
,- В первом примере проверяется, что элемент включен
- Второй пример чека на заказ тоже
Это сравнивает 2 несортированных массива:
function areEqual(a, b) {
if ( a.length != b.length) {
return false;
}
return a.filter(function(i) {
return !b.includes(i);
}).length === 0;
}
Простой
type Values = number | string;
/** Not sorted array */
function compare<Values>(a1: Array<Values>, a2: Array<Values>): boolean {
if (a1.length !== a2.length) {
return false;
}
/** Unique values */
const set1 = new Set<Values>(a1);
const set2 = new Set<Values>(a2);
if (set1.size !== set2.size) {
return false;
}
return [...set1].every((value) => [...set2].includes(value));
}
compare([1, 2, 3], [1, 2, 3]); // true
compare([1, 2, 3], [1, 3, 2]); // true
compare([1, 1, 1], [1, 2, 3]); // false
compare([1, 1, 2, 3], [1, 2, 3]); // false
/** Sorted arrays, faster method */
function compareSorted<Values>(a1: Array<Values>, a2: Array<Values>): boolean {
if (a1.length !== a2.length) {
return false;
}
/** Unique values */
const set1 = new Set<Values>(a1);
const set2 = new Set<Values>(a2);
if (set1.size !== set2.size) {
return false;
}
return [...set1].every((value, index) => value === [...set2][index]);
}
compareSorted([1, 2, 3], [1, 2, 3]); // true
compareSorted([1, 2, 3], [1, 3, 2]); // false
compareSorted([1, 1, 1], [1, 2, 3]); // false
compareSorted([1, 1, 2, 3], [1, 2, 3]); // false
Здесь возможность для несортированных массивов и пользовательского сравнения:
const array2 = [1,3,2,4,5];
const isInArray1 = array1.every(item => array2.find(item2 => item===item2))
const isInArray2 = array2.every(item => array1.find(item2 => item===item2))
const isSameArray = array1.length === array2.length && isInArray1 && isInArray2
console.log(isSameArray); //true
Уже есть несколько отличных ответов. Но я хотел бы поделиться другой идеей, которая доказала свою надежность при сравнении массивов. Мы можем сравнить два массива, используя JSON.stringify (). Он создаст строку из массива и, таким образом, сравнит две полученные строки из двух массивов на равенство.
JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:1},2]) //true
JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:2},2]) //false
JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:2},[3,4],2]) //false
JSON.stringify([1,{a:1},[3,4],2]) == JSON.stringify([1,{a:2},[3,4],2]) //false
JSON.stringify([1,{a:2},[3,4],2]) == JSON.stringify([1,{a:2},[3,4],2]) //true
JSON.stringify([1,{a:2},[3,4],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //false
JSON.stringify([1,{a:2},[3,4,[4]],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //false
JSON.stringify([1,{a:2},[3,4,[5]],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //true
In a simple way uning stringify but at same time thinking in complex arrays:
**Simple arrays**:
var a = [1,2,3,4];
var b = [4,2,1,4];
JSON.stringify(a.sort()) === JSON.stringify(b.sort()) // true
**Complex arrays**:
var a = [{id:5,name:'as'},{id:2,name:'bes'}];
var b = [{id:2,name:'bes'},{id:5,name:'as'}];
JSON.stringify(a.sort(function(a,b) {return a.id - b.id})) === JSON.stringify(b.sort(function(a,b) {return a.id - b.id})) // true
**Or we can create a sort function**
function sortX(a,b) {
return a.id -b.id; //change for the necessary rules
}
JSON.stringify(a.sort(sortX)) === JSON.stringify(b.sort(sortX)) // true
Мы могли бы сделать это функциональным способом, используя every
( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
function compareArrays(array1, array2) {
if (array1.length === array2.length)
return array1.every((a, index) => a === array2[index])
else
return false
}
// test
var a1 = [1,2,3];
var a2 = [1,2,3];
var a3 = ['a', 'r', 'r', 'a', 'y', '1']
var a4 = ['a', 'r', 'r', 'a', 'y', '2']
console.log(compareArrays(a1,a2)) // true
console.log(compareArrays(a1,a3)) // false
console.log(compareArrays(a3,a4)) // false