Как узнать, имеют ли два массива одинаковые значения


107

У меня есть два массива: один заполнен информацией из запроса ajax, а другой хранит кнопки, на которые нажимает пользователь. Я использую этот код (я набрал номера образцов):

var array1 = [2, 4];
var array2 = [4, 2]; //It cames from the user button clicks, so it might be disordered.
array1.sort(); //Sorts both Ajax and user info.
array2.sort();
if (array1==array2) {
    doSomething();
}else{
    doAnotherThing();
}

Но всегда дает false, даже если два массива одинаковые, но с разными именами. (Я проверил это в консоли Chrome JS). Итак, есть ли способ узнать, содержат ли эти два массива то же самое? Почему это дает false? Как узнать, каких значений в первом массиве нет во втором?


1
Я почти уверен, что вам нужно просмотреть каждый элемент массивов.
— Thomas Li

Вы знаете, почему он возвращает false? Любопытно.
— RobW 01


Ответы:


36
function arraysEqual(_arr1, _arr2) {

    if (!Array.isArray(_arr1) || ! Array.isArray(_arr2) || _arr1.length !== _arr2.length)
      return false;

    var arr1 = _arr1.concat().sort();
    var arr2 = _arr2.concat().sort();

    for (var i = 0; i < arr1.length; i++) {

        if (arr1[i] !== arr2[i])
            return false;

    }

    return true;

}

Обратите внимание, что это не изменяет исходные массивы, в отличие от предыдущего ответа.


2
сортировка занимает nlog (n) времени. Вам не нужна сортировка. Этот ответ stackoverflow.com/a/55614659/3209523 работает в линейном времени.
— canbax

Используя машинописный текст, Array.isArray () вызывал ошибки, поэтому он работал нормально.
— Ариэль Фришер,

89

Если элементы вашего массива не являются объектами - например, если они являются числами или строками, вы можете сравнить их соединенные строки, чтобы увидеть, имеют ли они одинаковые элементы в любом порядке -

var array1= [10, 6, 19, 16, 14, 15, 2, 9, 5, 3, 4, 13, 8, 7, 1, 12, 18, 11, 20, 17];
var array2= [12, 18, 20, 11, 19, 14, 6, 7, 8, 16, 9, 3, 1, 13, 5, 4, 15, 10, 2, 17];

if(array1.sort().join(',')=== array2.sort().join(',')){
    alert('same members');
}
else alert('not a match');

2
Это будет хорошо работать для примитивов или объектов, которые имеют однозначно идентифицирующие значения toString, но не для любых объектов.
— devios1

Спасибо! аккуратное решение
— Гастон Санчес

3
Остерегайтесь нулевых элементов и сортировки. В моем случае я столкнулся со строками для сравнения, такими как «, 2,2,3» и «2,2,3», которые, конечно, не строго равны.
— barbara.post

2
Может неудачу строк, то есть ['a', 'b']и ['a,b']. Я бы рекомендовал эту технику только для небольших одноразовых скриптов.
— Alex

1
@alex - в моем случае запятые в строках разрешены, а точки с запятой - нет, поэтому я использовал ';' присоединиться вместо запятой
— a2345 отправлено

45

Если вы хотите проверить, имеют ли два массива одинаковые значения (независимо от количества вхождений и порядка каждого значения), вы можете сделать это с помощью lodash :

_.isEmpty(_.xor(array1, array2))

Коротко, просто и красиво!


1
Кажется, я не могу найти xorв документах с подчеркиванием? Вы думаете об IODash?
— Патрик Менсиас-Льюис

44
Array.prototype.compare = function(testArr) {
    if (this.length != testArr.length) return false;
    for (var i = 0; i < testArr.length; i++) {
        if (this[i].compare) { //To test values in nested arrays
            if (!this[i].compare(testArr[i])) return false;
        }
        else if (this[i] !== testArr[i]) return false;
    }
    return true;
}

var array1 = [2, 4];
var array2 = [4, 2];
if(array1.sort().compare(array2.sort())) {
    doSomething();
} else {
    doAnotherThing();
}

Может быть?


Спасибо! Работает так, как хотелось. Я немного изменил функцию, чтобы также знать, сколько несовпадений.
— Carlos Precioso

false для [2,4] [4,2].
— Сураз Ханал

@SurazKhanal Еще нужно отсортировать
— Аарон Макмиллин

28

Почему ваш код не работал

В JavaScript есть примитивные типы данных и непримитивные типы данных.

Для примитивных типов данных ==и ===проверьте, имеют ли значения по обе стороны от полосок одинаковое значение. Вот почему 1 === 1верно.

Для непримитивных типов данных, таких как массивы, ==и ===проверьте равенство ссылок. То есть они проверяют, являются ли arr1и arr2являются одним и тем же объектом. В вашем примере два массива имеют одинаковые объекты в одном порядке, но не эквивалентны.

Решения

Два массива arr1и arr2имеют одинаковые члены тогда и только тогда, когда:

  • Все arr2вarr1

И

  • Все arr1вarr2

Итак, это поможет (ES2016):

const containsAll = (arr1, arr2) => 
                arr2.every(arr2Item => arr1.includes(arr2Item))

const sameMembers = (arr1, arr2) => 
                        containsAll(arr1, arr2) && containsAll(arr2, arr1);

sameMembers(arr1, arr2); // `true`

Это второе решение с использованием Underscore ближе к тому, что вы пытались сделать:

arr1.sort();
arr2.sort();

_.isEqual(arr1, arr2); // `true`

Он работает, потому что isEqualпроверяет «глубокое равенство», что означает, что он смотрит не только на ссылочное равенство и сравнивает значения.

Решение вашего третьего вопроса

Вы также спросили, как узнать, какие вещи arr1не содержатся в arr2.

Это сделает это (ES2015):

const arr1 = [1, 2, 3, 4];
const arr2 = [3, 2, 1];

arr1.filter(arr1Item => !arr2.includes(arr1Item)); // `[4]`

Вы также можете использовать differenceметод Underscore ::

_.difference(arr1, arr2); // `[4]`

ОБНОВИТЬ

См. Комментарий @ Redu - мое решение для sameMembers, но то, что вы, возможно, имеете в виду, sameMembersInOrderтакже известно как deepEquals.

ОБНОВЛЕНИЕ 2

Если вам не важен порядок членов массивов, ES2015 + Setможет быть лучшей структурой данных, чем Array. См. Примечания MDN о том, как реализовать isSupersetиdifference использовать опасные исправления для обезьян.


1
Ваши решения неверны. «Два массива, arr1 и arr2, имеют одинаковые члены тогда и только тогда, когда: все в arr2 находится в arr1 И все в arr1 находится в arr2», это также неверно. Это массив, а не набор. Так что sameMembers([1,1,2],[2,1,2]);должен вернуть false.
— Реду

1
@Redu полагаю, это зависит от того, что означает «одинаковые участники» - я понимаю, что это означает «имеет одинаковых участников». sameMembers([1,1,2],[2,1,2])должен вернуться true, на мой взгляд. sameMembersInOrder([1,1,2],[2,1,2])AKA deepEquals([1,1,2],[2,1,2])должен вернуться false.
— Макс Хейбер,

Ваше третье решение arr1.filter...будет работать только для проверки того, имеет ли arr2 все элементы arr1 или нет, но не наоборот, что также требуется.
— Аакаш Верма

10

Проверка равенства объектов:JSON.stringify(array1.sort()) === JSON.stringify(array2.sort())

Вышеупомянутый тест также работает с массивами объектов, и в этом случае используйте функцию сортировки, как описано в http://www.w3schools.com/jsref/jsref_sort.asp

Может хватить для небольших массивов с плоскими схемами JSON.


9

Наша цель - проверить, являются ли 2 массива равными множествами. набор - это математически определенный набор . Самая быстрая асимптотическая сортировка занимает O (nlog (n)) времени. Итак, если вы отсортируете массив, это займет как минимум O (nlog (n)) времени. Но вы можете выполнить эту задачу быстрее , что асимптотически занимает время O (n) (средний случай, а не худший случай) со структурой данных словаря. В JS словарь - это просто объект с ключами и значениями.

/** assumes array elements are primitive types
* check whether 2 arrays are equal sets.
* @param  {} a1 is an array
* @param  {} a2 is an array
*/
function areArraysEqualSets(a1, a2) {
  const superSet = {};
  for (const i of a1) {
    const e = i + typeof i;
    superSet[e] = 1;
  }

  for (const i of a2) {
    const e = i + typeof i;
    if (!superSet[e]) {
      return false;
    }
    superSet[e] = 2;
  }

  for (let e in superSet) {
    if (superSet[e] === 1) {
      return false;
    }
  }

  return true;
}

Обратите внимание , что эта функция работает с массивами примитивных типов и предполагает a1и a2массивы.


Это не относится к случаю, когда в одном из массивов есть повторяющиеся значения. напримерareArraysEqualSets([1, 2, 2], [1, 2])
— Крис Кент

Оно делает. Пробовал в консоли. areArraysEqualSets([1, 2, 2], [1, 2]) trueМатематически они равны.
— canbax

6

Как насчет этого? ES 2017 я полагаю:

const array1 = [1, 3, 5];
const array2 = [1, 5, 3];

const isEqual = (array1.length === array2.length) && (array1.every(val => array2.includes(val)));
console.log(isEqual);

Первое условие проверяет, имеют ли оба массива одинаковую длину, а второе условие проверяет, является ли 1-й массив подмножеством 2-го массива. Объединение этих двух условий должно привести к сравнению всех элементов двух массивов независимо от порядка элементов.

Приведенный выше код будет работать только в том случае, если оба массива не имеют повторяющихся элементов.


3

Когда вы сравниваете эти два массива, вы сравниваете объекты, представляющие массивы, а не содержимое.

Вам нужно будет использовать функцию для сравнения этих двух. Вы можете написать свой собственный, который просто зацикливается на одном и сравнивает его с другим после того, как вы проверите, что длины одинаковы.


3

Простое решение для поверхностного равенства с использованием ES6:

const arr1test = arr1.slice().sort()
const arr2test = arr2.slice().sort()
const equal = !arr1test.some((val, idx) => val !== arr2test[idx])

Создает мелкие копии каждого массива и сортирует их. Затем используется some()для перебора arr1testзначений, сравнивая каждое значение со значением arr2testс тем же индексом. Если все значения равны, some()возвращается falseи, в свою очередь, equalоценивается как true.

Можно также использовать every(), но ему придется перебирать каждый элемент в массиве, чтобы получить trueрезультат, тогда как он some()откажется, как только найдет значение, которое не равно:

const equal = arr1test.every((val, idx) => val === arr2test[idx])

2

У меня были простые целочисленные значения в игровом проекте.
Меньшее количество значений в каждом массиве также нуждалось в том, чтобы исходный массив оставался нетронутым.
Итак, я сделал следующее, все работало нормально. (Код отредактирован для вставки сюда)

var sourceArray = [1, 2, 3];
var targetArray = [3, 2, 1];

if (sourceArray.length !== targetArray.length) {
    // not equal
    // did something
    return false;
}

var newSortedSourceArray = sourceArray.slice().sort();
var newSortedTargetArray = targetArray.slice().sort();

if (newSortedSourceArray.toString() !== newSortedTargetArray.toString()) { // MAIN CHECK
    // not equal
    // did something
    return false;
}
else {
    // equal
    // did something
    // continued further below
}

// did some more work

return true;

Надеюсь, это поможет.


2

Использование ES6

Мы будем использовать equalsфункцию Ramda , но вместо этого мы можем использовать Lodash или Underscore isEqual:

const R = require('ramda');

const arraysHaveSameValues = (arr1, arr2) => R.equals( [...arr1].sort(), [...arr2].sort() )

Используя оппонент распространения, мы избегаем мутации исходных массивов и сохраняем нашу функцию чистой.


2

Вы можете использовать reduceвместо циклов, чтобы казаться умным, но рискуете, что ваши коллеги-разработчики будут думать о вас как о умнике.

function isArrayContentSame(a, b) {
  if (Array.isArray(a) && Array.isArray(b) && a.length == b.length) {
    a = a.concat().sort()
    b = b.concat().sort()
    return a.reduce((acc,e,i) => acc && e === b[i], true)
  } else {
    return false;
  }
}

1

Если элементы в массиве являются примитивами (числами или одиночными символами), вы можете использовать комбинацию сравнения длин и использования наборов.

function equalArrayItems(arr1, arr2) {
  if (arr1.length !== arr2.length) return false
  const set1 = new Set(arr1)
  const set2 = new Set(arr2)
  const set3 = new Set(arr1, arr2)
  return set1.size === set3.size && set2.size === set3.size
}

Это не обрабатывает случаи с разными повторяющимися значениями. напримерequalArrayItems([1, 2, 2], [1, 1, 2])
— Крис Кент

1

Большинство других решений используют сортировку, O (n * log n), используют библиотеки или имеют сложность O (n ^ 2).

Вот чистое решение Javascript с линейной сложностью O (n):

/**
 * Check if two arrays of strings or numbers have the same values
 * @param {string[]|number[]} arr1
 * @param {string[]|number[]} arr2
 * @param {Object} [opts]
 * @param {boolean} [opts.enforceOrder] - By default (false), the order of the values in the arrays doesn't matter.
 * @return {boolean}
 */
function compareArrays(arr1, arr2, opts) {

  function vKey(i, v) {
    return (opts?.enforceOrder ? `${i}-` : '') + `${typeof v}-${v}`
  }

  if (arr1.length !== arr2.length) return false;

  const d1 = {};
  const d2 = {};

  for (let i = arr1.length - 1; i >= 0; i--) {
    d1[vKey(i, arr1[i])] = true;
    d2[vKey(i, arr2[i])] = true;
  }

  // Check that all items in arr1 are in arr2
  for (let i = arr1.length - 1; i >= 0; i--) {
    const v = vKey(i, arr1[i]);
    if (d1[v] !== d2[v]) return false;
  }

  // Check that all items in arr2 are in arr1
  for (let i = arr2.length - 1; i >= 0; i--) {
    const v = vKey(i, arr2[i]);
    if (d1[v] !== d2[v]) return false;
  }

  return true
}

Тесты:

arr1= (2) [1, 2]
arr2= (2) [1, 2]
compareArrays(arr1, arr2) => true
compareArrays(arr1, arr2, {enforceOrder: true}) => true
-------
arr1= (2) [1, 2]
arr2= (2) [2, 1]
compareArrays(arr1, arr2) => true
compareArrays(arr1, arr2, {enforceOrder: true}) => false
-------
arr1= (2) [2, 1]
arr2= (2) [1, 2]
compareArrays(arr1, arr2) => true
compareArrays(arr1, arr2, {enforceOrder: true}) => false
-------
arr1= (2) [2, 2]
arr2= (2) [1, 2]
compareArrays(arr1, arr2) => false
compareArrays(arr1, arr2, {enforceOrder: true}) => false
-------
arr1= (2) [1, 2]
arr2= (2) [2, 2]
compareArrays(arr1, arr2) => false
compareArrays(arr1, arr2, {enforceOrder: true}) => false
-------
arr1= ["1"]
arr2= [1]
compareArrays(arr1, arr2) => false
compareArrays(arr1, arr2, {enforceOrder: true}) => false
-------
arr1= (2) ["1", 2]
arr2= (2) [2, "1"]
compareArrays(arr1, arr2) => true
compareArrays(arr1, arr2, {enforceOrder: true}) => false
-------
arr1= []
arr2= []
compareArrays(arr1, arr2) => true
compareArrays(arr1, arr2, {enforceOrder: true}) => true

1
Эй, отличная запись! Это довольно запутанно, но имеет смысл. У меня вопрос: я не очень хорошо знаком с нотацией Big O, но наверняка это алгоритм O (2n)? Не думайте, что это имеет большое значение.
— Карлос Прециозо,

1
@CarlosPrecioso Это верно и O (2n) = O (n). Сложность не меняется при умножении на постоянный коэффициент
— SC1000

Это не касается таких случаев, как compareArrays(['1'], [1])илиcompareArrays([2, 2], [1, 2])
— Крис Кент

@ SC1000 В моих примерах есть только массивы строк или чисел.
— Крис Кент

@ SC1000 Действительно. В каждом примере массивы имеют разные значения, но функция возвращает true. Ожидаемый результат сравнения ['1'] с [1] будет ложным, поскольку массивы имеют разные значения. Также [2, 2] и [1, 2] - это массивы с разными значениями, поэтому ожидаемый результат ложный. Кроме того, если вы это сделаете, compareArrays([1, 2], [2, 2])он вернет false, поэтому ваша функция не коммутативна; Что, как мне кажется, неожиданно для такой функции.
— Крис Кент

0

Если вы используете Prototype Framework, вы можете использовать метод пересечения массива, чтобы узнать, что они одинаковы (независимо от порядка):

var array1 = [1,2];
var array2 = [2,1];

if(array1.intersect(array2).length === array1.length) {
    alert("arrays are the same!");
}

Не работает - [1,2].intersect([1,2,3]).length === [1,2].lengthвозвращает истину. Вам также следует сравнить длину исходных массивов, для демонстрации я отредактировал сообщение.
— GMA

На самом деле я только что понял, что предложенное мной изменение не работает в случае дублирования ... например, оно вернет false для array1 = [1,1,2]; array2 = [1,1,2];... исходный ответ не терпит неудачу для этого ввода.
— GMA

Вы можете сделать обратное с_.difference(array1, array2).length;
— Вик

0

пожалуйста, проверьте этот ответ

var arr1= [12,18];
var arr2= [12, 18, 20, 11, 19, 14, 6, 7, 8, 16, 9, 3, 1, 13, 5, 4, 15, 10, 2, 17];
for(i=0;i<arr1.length;i++)
{
var array1=arr1[i];
for(j=0;j<arr2.length;j++)
{
    var array2=arr2[j];
    if(array1==array2)
    {
return true;
    }
}
}

2
Функционально это эквивалентно этому ответу , за исключением пары ошибок. Во-первых, все это должно быть заключено в функцию, иначе это returnне повлияет. Во-вторых, вы должны проверить отсортированные массивы, так как [1,2]и [2,1]будут определены как не совпадающие. В-третьих, что наиболее важно, это фактически только проверяет, является ли какой-то элемент таким же. Условное должно быть if (array1!==array2) {return false;}. Может быть, это поможет тебе в будущем!
— Карлос Прециозо

1
И в качестве дополнительного комментария попробуйте использовать отступы для лучшего понимания потока кода, а также более четкие имена переменных. Например: array1и array2можно было переименовать в elem1и elem2. Оба эти совета избавят вас от головной боли в будущем!
— Карлос Прециозо,

2
При дальнейшем осмотре, почему двойная петля? Оба массива должны быть одинаковой длины, а если нет, то они прямо не равны. Таким образом, вы можете использовать только один цикл. Прямо сейчас этот код проверяет, находятся ли какие-либо элементы первого массива где-либо во втором. Проверьте этот ответ, чтобы узнать, как вы должны его реализовать. Удачи в пути к JavaScript!
— Карлос Прециозо,

0

Отвечаю спустя долгое время, но надеюсь, что это поможет кому-то, кто ищет простое решение и современных новичков.

Теперь мы можем добиться этого с помощью нескольких библиотек, таких как lodash, underscoreи т. Д. (В настоящее время они становятся частью проекта из-за простоты, множества функций и высокой степени использования).

Вы можете использовать пересечение из библиотеки lodash.

_.intersection(['2-1', '1'], ['2-2', '3-1', '2-1']); 
// => ['2-1']

Это будет работать для любого типа данных ..


0

Если вы хотите сравнить два массива и проверить, совпадает ли какой-либо объект в обоих массивах, он будет работать. Пример :

Array1 = [a, b, c, d]
Array2 = [d, e, f, g]

Здесь 'd' является общим в обоих массивах, поэтому эта функция вернет истинное значение.

  cehckArray(array1, array2) {
    for (let i = 0; i < array1.length; i++) {
      for (let j = 0; j < array2.length; j++) {
        if (array1[i] === array2[j]) {
          return true;
        }
      }
    }
    // Return if no common element exist 
    return false;
  }

0

Попробуй это

function arraysEqual(arr1, arr2){
    if (!Array.isArray(arr1) || !Array.isArray(arr2) || arr1.length!=arr2.length)
        return false;

    return arr1.length==arr1.filter(word => arr2.includes(word)).length;
}

0

У меня есть другой способ, основанный на принятом ответе.

function compareArrays(array1, array2) {

    if (
        !Array.isArray(array1)
        || !Array.isArray(array2)
        || array1.length !== array2.length
    ) return false;

    var first = array1.sort().map(value => (String(value))).join();
    var second = array2.sort().map(value => (String(value))).join();

    return first == second ? true : false;
}

Привет, добро пожаловать в StackOverflow! Хотя в некоторых случаях этот ответ сработает, в некоторых конкретных случаях это не так. Во-первых, помните, что .sort () изменяет исходный массив. В настоящее время это считается плохой гигиеной, поэтому в исходном ответе сначала выполняется .concat (), чтобы сделать копию.
— Карлос Прециозо,

И, во-вторых, это не будет работать согласованно с остальной частью JavaScript. {foo: "bar"} === {foo: "bar"} возвращает false (это два разных объекта, созданных отдельно); поэтому compareArrays ([{foo: "bar"}], [{foo: "bar"}]) также должен возвращать false для согласованности. Однако в вашей реализации он возвращает истину, поскольку строковое представление объектов такое же. Это может быть желаемое поведение или нет, но в любом случае о нем следует помнить.
— Карлос Прециозо,

0

Функция сравнения двух массивов, чтобы проверить, имеют ли оба одинаковые элементы. Даже если они вышли из строя ...

Это хорошо для простых массивов. [String, Number, Boolean, null, NaN].

Я не использую .sort (), он изменяет исходный массив. Некоторые говорят, что это плохо ...

Осторожно. Эта функция ограничена, она не может сравнивать объекты "[], {}" или функции в этих массивах, массивы сами по себе являются объектами.

   let arraysHasSameElements = (arr1, arr2) => {
        let count =
            // returns counting of occurrences.
            (arr, val) => arr.reduce((count, curr) => (curr === val ? 1 : 0) + count, 0);

        /* this will return true if lengths of the arrays is equal.
           then compare them.*/
        return arr1.length === arr2.length

            // compare arr1 against arr2.
            && arr1.reduce((checks, val) =>

                /*  creating array of checking if a value has equal amount of occurrences
                    in both arrays, then adds true 'check'. */
                checks.concat(count(arr1, val) === count(arr2, val)), [])

                // checking if each check is equal to true, then .every() returns true.
                .every(check => check);
    }

    let arr1 = ['',-99,true,NaN,21,null,false,'help',-99,'help',NaN], 
        arr2 = [null,-99,'',NaN,NaN,false,true,-99,'help',21,'help'];
    arraysHasSameElements(arr1, arr2); //true

    let arr3 = [false,false,false,false,false,false], 
        arr4 = [false,false,false,false,false,false]
    arraysHasSameElements(arr3, arr4); //true


    // here we have uncommented version.
    let arraysHasSameElements = (arr1, arr2) => {
        let count = (arr, val) => arr.reduce((count, curr) => (curr === val ? 1:0) + count, 0);
        return arr1.length === arr2.length && arr1.reduce((checks, val) =>
            checks.concat(count(arr1, val) === count(arr2, val)), []).every(check => check);
    }

-1

Простое решение для сравнения двух массивов:

var array1 = [2, 4];
var array2 = [4, 2];

array1.sort();
array2.sort();

if (array1[0] == array2[0]) {
    console.log("Success");
}else{
    console.log("Wrong");
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.