Как проверить, совпадают ли два объекта Map?

Как я могу проверить, имеют ли два ES2015 Map тот же набор пар (key, value)?

Мы можем предположить, что все ключи и значения являются примитивными типами данных.

Один из подходов к решению этого вопроса - взять map.entries(), создать массив из него, а затем отсортировать этот массив по ключам. И сделайте то же самое с другой картой. И затем проведите через эти два массива, чтобы сравнить их. Все эти швы громоздки, а также очень неэффективны из-за сортировки (неэффективность производительности) и из-за создания этих массивов (неэффективность памяти).

Есть ли у кого-нибудь идеи?

Ответ 1

Не существует "стандартного" или "встроенного" способа сделать это. Концептуально, вы просто должны сравнить, что два объекта Map имеют одинаковые ключи и значения для каждого ключа и не имеют дополнительных ключей.

Чтобы сравнение было максимально эффективным, вы можете выполнить следующие оптимизации:

  1. Сначала проверьте свойство .size на обеих картах. Если две карты не имеют одинаковое количество ключей, то вы сразу знаете, они не могут быть идентичными.
  2. Кроме того, гарантируя, что они имеют одинаковое количество ключей, вы можете просто перебрать одну из карт и сравнить ее значения с другой.
  3. Используйте синтаксис итератора for (var [key, val] of map1) для итерации ключей, чтобы вам не приходилось создавать или сортировать массив ключей самостоятельно (это должно быть как быстрее, так и более эффективно использовать память).
  4. Затем, наконец, если вы убедитесь, что сравнение возвращается сразу же, как только обнаружено несоответствие, тогда оно сократит время выполнения, когда они не совпадают.

Затем, поскольку undefined является допустимым значением в Map, но также и то, что .get() возвращает, если ключ не найден, мы должны следить за этим, выполняя дополнительный .has() если сравниваемое значение не undefined

Поскольку и ключи, и значения с объектом Map сами могут быть объектами, это становится намного сложнее, если вы хотите провести глубокое сравнение свойств объектов для определения равенства, а не просто более простой === который Javascript использует по умолчанию для проверки того же объекта., Или, если вас интересуют только объекты, у которых есть примитивы для ключей и значений, то этой сложности можно избежать.

Для функции, которая проверяет только строгое равенство значений (проверяет, являются ли они одним и тем же физическим объектом, а не выполняет глубокое сравнение свойств), вы можете сделать то, что показано ниже. При этом используется синтаксис ES6 для эффективной итерации объектов карты и попытки повысить производительность, когда они не совпадают, путем короткого замыкания и возврата false как только обнаруживается несоответствие.

Для этого фрагмента требуется Firefox 41 или Chrome 49. Он не работает в Edge 25 или IE 11 (возможно, из-за пользователя с типом синтаксиса for/of ES6, который он использует). Его можно заставить работать в других браузерах, используя более старую технологию для цикла for, но поскольку в любом случае речь идет о функции ES6 (объект Map), и мы пытаемся оптимизировать реализацию, я решил использовать последнюю версию ES6. синтаксис.

"use strict";

function compareMaps(map1, map2) {
    var testVal;
    if (map1.size !== map2.size) {
        return false;
    }
    for (var [key, val] of map1) {
        testVal = map2.get(key);
        // in cases of an undefined value, make sure the key
        // actually exists on the object so there are no false positives
        if (testVal !== val || (testVal === undefined && !map2.has(key))) {
            return false;
        }
    }
    return true;
}

// construct two maps that are initially identical
var o = {"k" : 2}

var m1 = new Map();
m1.set("obj", o);
m1.set("str0", undefined);
m1.set("str1", 1);
m1.set("str2", 2);
m1.set("str3", 3);

var m2 = new Map();
m2.set("str0", undefined);
m2.set("obj", o);
m2.set("str1", 1);
m2.set("str2", 2);
m2.set("str3", 3);

log(compareMaps(m1, m2));

// add an undefined key to m1 and a corresponding other key to m2
// this will pass the .size test and even pass the equality test, but not pass the
// special test for undefined values
m1.set("str-undefined", undefined);
m2.set("str4", 4);
log(compareMaps(m1, m2));

// remove one key from m1 so m2 has an extra key
m1.delete("str-undefined");
log(compareMaps(m1, m2));

// add that same extra key to m1, but give it a different value
m1.set("str4", 5);
log(compareMaps(m1, m2));

function log(args) {
    var str = "";
    for (var i = 0; i < arguments.length; i++) {
        if (typeof arguments[i] === "object") {
            str += JSON.stringify(arguments[i]);
        } else {
            str += arguments[i];
        }
    }
    var div = document.createElement("div");
    div.innerHTML = str;
    var target = log.id ? document.getElementById(log.id) : document.body;
    target.appendChild(div);
}

Ответ 2

Если ваш Map имеет строковые ключи только, вы можете использовать этот подход для их сравнения:

const mapToObj = (map) => {
  let obj = Object.create(null)
  for (let [k,v] of map) {
    // We don’t escape the key '__proto__'
    // which can cause problems on older engines
    obj[k] = v
  }
  return obj
}

assert.deepEqual(mapToObj(myMap), myExpectedObj)

Примечание. deepEqual является частью множества наборов для тестирования, а если нет, вы можете использовать эквиваленты lodash/underscore. Любая функция, которая делает глубокое сравнение, будет делать.

mapToObj функция любезно предоставлена ​​http://exploringjs.com/es6/ch_maps-sets.html