javascript - iterarea unui map TypeScript eșuează


Folosesc funcția de mai jos pentru a compara două hărți. Ceea ce este interesant este că codul din interiorul buclei for nu este niciodată executat. Deci, codul console.log(key,val) nu este niciodată executat. Desigur, m-am asigurat că hărțile pe care le compar nu sunt goale și au aceeași dimensiune pentru a forța executarea codului din interiorul buclei for. Fac o greșeală foarte simplă sau îmi scapă un concept profund?

private compareMaps(map1, map2) {
        var testVal;
        if (!(map1 && map2)) {
            return false;
        }
        if (map1.size !== map2.size) {
            return false;
        }
        for (var [key, val] of map1) {
            testVal = map2.get(key);
            console.log(key, val);
            if (testVal !== val || (testVal === undefined && !map2.has(key))) {
                return false;
            }
        }
        return true;
    }

Răspuns #1

Ceea ce este interesant este că codul din interiorul buclei for nu este niciodată executat. Fac o greșeală foarte prostească sau îmi scapă un concept profund?

Îți lipsește faptul că...

for...of nu funcționează pe Map în TypeScript înainte de ES6

Când vizează ECMAScript anterior ES6, compilatorul TypeScript transpilează instrucțiunea for...of într-un buclă for.

Acest cod:

for (var [key, val] of map) {
    console.log(key);
}

Devine acest cod:

for (var _i = 0, map_1 = map; _i < map_1.length; _i++) {
    var _a = map_1[_i], key = _a[0], val = _a[1];
    console.log(key);
}

Ce să fac?

Opțiunea 1: Când trebuie să vizăm ECMAScript anterior ES6, funcția forEach poate fi o alternativă potrivită la bucla for...of. Atenție totuși că forEach are diferențe subtile față de for...of (de exemplu, ieșirea timpurie, async/await).

map.forEach((val, key) => { 
    console.log(key + ":" + val);
});

Opțiunea 2: Când trebuie să vizăm ECMAScript înainte de ES6 și trebuie să avem comportamentul exact al for...of, răspunsul lui John Weisz are sens: convertește Map în Array și iterează Array cu for...of.

Opțiunea 3: Când putem să țintim ECMAScript ES6 sau versiuni ulterioare, folosiți bucla for...of direct pe Map.

În paranteză: Egalitatea hărților

Dacă cerința este egalitatea hărților, ia în considerare funcția every. Aceasta se ocupă de necesitatea de a break în iterație, deoarece every returnează imediat când găsește primul false.

Dacă ordinea contează pentru comparație, folosește every astfel:

function areEqual<K,V>(map1: Map<K,V>, map2: Map<K,V>) { 
    if(!map1 || !map2) return false;
    const array1 = Array.from(map1.entries());
    const array2 = Array.from(map2.entries());
    return array1.every(([k1, v1], index) => {
        const [k2, v2] = array2[index];
        return k1 === k2 && v1 === v2;
    });
}

Dacă ordinea nu contează, atunci dorim egalitatea mulțimilor; folosește every astfel:

function areSetEqual<K, V>(map1: Map<K, V>, map2: Map<K, V>) { 
    if(!map1 || !map2) return false;
    const array1 = Array.from(map1.entries());
    const array2 = Array.from(map2.entries());
    return array1.length === array2.length &&
        array1.every(([k1, v1]) => map2.get(k1) === v1);
}

Iată un demo al funcțiilor în acțiune:

const map1 = new Map([["key1", "val1"], ["key2", "val2"]]);
const map2 = new Map([["key1", "val1"], ["key2", "val2"]]);
const map3 = new Map([["key2", "val2"], ["key1", "val1"]]);

console.log(areEqual(map1, map2)); // true
console.log(areEqual(map1, map3)); // false
console.log(areSetEqual(map1, map2)); // true
console.log(areSetEqual(map1, map3)); // true

Răspunsul #2

O buclă for ... of poate parcurge un iterabil, iar Map<K, V> prin el însuși nu este un iterabil. Poți să-l convertești într-un iterabil folosind fie metoda keys, values sau entries (pe care apoi le poți converti mai departe într-un array).

De exemplu, pentru a obține un array care să reprezinte cheile hărții tale în ordinea în care au fost adăugate, poți:

let keys = Array.from(map1.keys());

Poți apoi să iterezi peste acest array keys folosind un ciclu for ... of, deoarece un array este iterabil:

for (var key of keys) {
    testVal = map2.get(key);
    console.log(key, val);

    if (testVal !== val || (testVal === undefined && !map2.has(key))) {
        return false;
    }
}

Interesant este faptul că, totuși, cred că ar trebui să fie posibil să realizezi acest lucru fără a fi nevoie să creezi array-uri, adică pur și simplu să folosești for (let key of map1.keys()), deoarece .keys() returnează de unul singur un obiect iterabil, dar TypeScript pare să aibă probleme în a gestiona asta (cel puțin în ES5, nu am încercat în ținta ES6).