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


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.