arrays поиск массиве - Как сравнить массивы в JavaScript?





15 Answers

Хотя это работает только для скалярных массивов (см. Примечание ниже), это коротко:

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

js двумерный сравнение

Я бы хотел сравнить два массива ... идеально, эффективно. Ничего необычного, просто 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 кодирует каждый массив, но есть ли более быстрый или «лучший» способ простого сравнения массивов без необходимости повторения каждого значения?




Это, я думаю, самый простой способ сделать это, используя JSON stringify, и это может быть лучшим решением в некоторых ситуациях:

JSON.stringify(a1) === JSON.stringify(a2);

Это преобразует объекты a1 и a2 в строки, чтобы их можно было сравнить. Порядок в большинстве случаев важен, поскольку он может сортировать объект с помощью алгоритма сортировки, показанного в одном из приведенных выше ответов.

Обратите внимание, что вы больше не сравниваете объект, а строковое представление объекта. Это может быть не совсем то, что вы хотите.




Практический путь

Я думаю, что неправильно сказать, что конкретная реализация - это «Правильный путь», если это только «правильный» («правильный»), в отличие от «неправильного» решения. Решение Tomáš является явным улучшением по сравнению с сопоставлением массивов на основе строк, но это не значит, что оно объективно «правильно». Что все равно? Это самый быстрый? Является ли это наиболее гибким? Легче ли это понять? Это быстрее отлаживается? Использует ли он наименьшие операции? Есть ли побочные эффекты? Ни одно решение не может иметь лучшее из всего.

Томаш мог сказать, что его решение быстро, но я бы тоже сказал, что это бесполезно сложно. Он пытается быть решением «все-в-одном», которое работает для всех массивов, вложенных или нет. Фактически, он даже принимает больше, чем просто массивы, как входные данные и все еще пытается дать «правильный» ответ.

Generics предлагают повторное использование

Мой ответ подойдет к проблеме по-разному. Я начну с общей процедуры arrayCompare , которая касается только 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 (Number) с '1' (String), результат будет 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 ). Это позволяет нам сохранять глубокое сравнительное поведение отдельно от того, как мы фактически сравниваем отдельные элементы. Т.е., как показано в приведенном выше примере, мы можем looseEqual глубокое сравнение с использованием equal , looseEqual или любого другого компаратора, который мы делаем.

Поскольку arrayDeepCompare находится в 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

Просто как тот. Здесь я использовал объекты Vanilla 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 больше, чем каждый ...

// 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 на крошечные процедуры. Это интересное упражнение, но не самый лучший (самый практичный) способ подойти к этой проблеме. Если вам интересно, вы можете увидеть эту историю изменений.




В духе оригинального вопроса:

Я бы хотел сравнить два массива ... идеально, эффективно . Ничего необычного , просто правда, если они идентичны, и ложные, если нет.

Я проводил тесты производительности по некоторым более простым предложениям, предложенным здесь, со следующими results (быстро до медленных):

while (67%) Тимом Даун

var i = a1.length;
while (i--) {
    if (a1[i] !== a2[i]) return false;
}
return true

every (69%) пользователем2782196

a1.every((v,i)=> v === a2[i]);

reduce (74%) по DEI

a1.reduce((a, b) => a && a2.includes(b), true);

join & toString (78%) от Gaizka Allende & vivek

a1.join('') === a2.join('');

a1.toString() === a2.toString();

половина toString (90%) от Виктора Паломо

a1 == a2.toString();

stringify (100%) от radtek

JSON.stringify(a1) === JSON.stringify(a2);

Обратите внимание, что приведенные ниже примеры предполагают, что массивы отсортированы, одномерные массивы. .length сравнение было удалено для общего теста (добавьте a1.length === a2.length к любому из предложений, и вы получите повышение производительности на 10%). Выберите любые решения, которые лучше всего подходят для вас, зная скорость и ограничение каждого из них.

Несвязанное примечание: интересно видеть, как люди получают все счастливые слова Джона Уэйна на кнопке с правом голоса на совершенно законных ответах на этот вопрос.




Если это только два массива чисел или строк, это быстрый однострочный

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



Если вы используете платформу тестирования, такую ​​как Mocha с библиотекой утверждения Chai , вы можете использовать deep равенство для сравнения массивов.

expect(a1).to.deep.equal(a2)

Это должно возвращать true, только если массивы имеют равные элементы по соответствующим индексам.




Решение Herer:

/**
 * Tests two data structures for equality
 * @param {object} x
 * @param {object} y
 * @returns {boolean}
 */
var equal = function(x, y) {
    if (typeof x !== typeof y) return false;
    if (x instanceof Array && y instanceof Array && x.length !== y.length) return false;
    if (typeof x === 'object') {
        for (var p in x) if (x.hasOwnProperty(p)) {
            if (typeof x[p] === 'function' && typeof y[p] === 'function') continue;
            if (x[p] instanceof Array && y[p] instanceof Array && x[p].length !== y[p].length) return false;
            if (typeof x[p] !== typeof y[p]) return false;
            if (typeof x[p] === 'object' && typeof y[p] === 'object') { if (!equal(x[p], y[p])) return false; } else
            if (x[p] !== y[p]) return false;
        }
    } else return x === y;
    return true;
};

Работает с любой вложенной структурой данных и, очевидно, игнорирует методы объектов. Даже не думайте о расширении Object.prototype с помощью этого метода, когда я попробовал это один раз, jQuery сломался;)

Для большинства массивов он все же быстрее, чем большинство решений для сериализации. Это, вероятно, самый быстрый метод сравнения для массивов записей объектов.




Это сравнивает 2 несортированных массива:

function areEqual(a, b) {
  if ( a.length != b.length) {
    return false;
  }
  return a.filter(function(i) {
    return !b.includes(i);
  }).length === 0;  
}



Расширение идеи Томаша Зато. Tomas Array.prototype.compare должен быть infact, называемый Array.prototype.compareIdentical.

Он проходит:

[1, 2, [3, 4]].compareIdentical ([1, 2, [3, 2]]) === false;
[1, "2,3"].compareIdentical ([1, 2, 3]) === false;
[1, 2, [3, 4]].compareIdentical ([1, 2, [3, 4]]) === true;
[1, 2, 1, 2].compareIdentical ([1, 2, 1, 2]) === true;

Но не удается:

[[1, 2, [3, 2]],1, 2, [3, 2]].compareIdentical([1, 2, [3, 2],[1, 2, [3, 2]]])

Здесь лучше (на мой взгляд) версия:

Array.prototype.compare = 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;

    this.sort();
    array.sort();
    for (var i = 0; i < this.length; i++) {
        // Check if we have nested arrays
        if (this[i] instanceof Array && array[i] instanceof Array) {
            // recurse into the nested arrays
            if (!this[i].compare(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;
}

http://jsfiddle.net/igos/bcfCY/




Эта функция сравнивает два массива произвольной формы и dimesionality:

function equals(a1, a2) {

    if (!Array.isArray(a1) || !Array.isArray(a2)) {
        throw new Error("Arguments to function equals(a1, a2) must be arrays.");
    }

    if (a1.length !== a2.length) {
        return false;
    }

    for (var i=0; i<a1.length; i++) {
        if (Array.isArray(a1[i]) && Array.isArray(a2[i])) {
            if (equals(a1[i], a2[i])) {
                continue;
            } else {
                return false;
            }
        } else {
            if (a1[i] !== a2[i]) {
                return false;
            }
        }
    }

    return true;
}



В моем случае сравниваемые массивы содержат только числа и строки. Эта функция покажет вам, содержат ли массивы одинаковые элементы.

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



JSON.stringify(collectionNames).includes(JSON.stringify(sourceNames)) ?  array.push(collection[i]) : null

Вот как я это сделал.




Вот версия машинописного текста:

//https://.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://.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)
})



function compareArrays(arrayA, arrayB) {
    if (arrayA.length != arrayB.length) return true;
    for (i = 0; i < arrayA.length; i++)
        if (arrayB.indexOf(arrayA[i]) == -1) {
            return true;
        }
    }
    for (i = 0; i < arrayB.length; i++) {
        if (arrayA.indexOf(arrayB[i]) == -1) {
            return true;
        }
    }
    return false;
}



Вот версия CoffeeScript, для тех, кто предпочитает это:

Array.prototype.equals = (array) ->
  return false if not array # if the other array is a falsy value, return
  return false if @length isnt array.length # compare lengths - can save a lot of time

  for item, index in @
    if item instanceof Array and array[index] instanceof Array # Check if we have nested arrays
      if not item.equals(array[index]) # recurse into the nested arrays
        return false
    else if this[index] != array[index]
      return false # Warning - two different object instances will never be equal: {x:20} != {x:20}
  true

Все кредиты принадлежат @ tomas-zato.




Related