webentwicklung-frage-antwort-db.com.de

Einfachste Möglichkeit, ES6 Maps/Sets zusammenzuführen

Gibt es eine einfache Möglichkeit, ES6 Maps zusammenzuführen (wie Object.assign)? Und wenn wir gerade dabei sind, was ist mit ES6-Sets (wie Array.concat)?

103
jameslk

Für Sets:

var merged = new Set([...set1, ...set2, ...set3])

Für Karten:

var merged = new Map([...map1, ...map2, ...map3])

Wenn mehrere Karten denselben Schlüssel haben, ist der Wert der zusammengeführten Karte der Wert der zuletzt zusammengeführten Karte mit diesem Schlüssel.

170
Oriol

Hier ist meine Lösung mit Generatoren:

Für Karten:

let map1 = new Map(), map2 = new Map();

map1.set('a', 'foo');
map1.set('b', 'bar');
map2.set('b', 'baz');
map2.set('c', 'bazz');

let map3 = new Map(function*() { yield* map1; yield* map2; }());

console.log(Array.from(map3)); // Result: [ [ 'a', 'foo' ], [ 'b', 'baz' ], [ 'c', 'bazz' ] ]

Für Sets:

let set1 = new Set(['foo', 'bar']), set2 = new Set(['bar', 'baz']);

let set3 = new Set(function*() { yield* set1; yield* set2; }());

console.log(Array.from(set3)); // Result: [ 'foo', 'bar', 'baz' ]
33
jameslk

Aus Gründen, die ich nicht verstehe, können Sie den Inhalt eines Sets nicht direkt mit einem integrierten Vorgang einem anderen hinzufügen. 

Es ist natürlich, dass .add() erkannt hat, dass Sie ein anderes Set-Objekt übergeben haben, und dann einfach alle Elemente aus dem Set (so wie mein eigenes Set-Objekt -, bevor es eine ES6-Set-Spezifikation gab) funktioniert . Aber sie beschlossen, es nicht so umzusetzen.

Stattdessen können Sie dies mit einer einzigen .forEach()-Zeile tun:

var s = new Set([1,2,3]);
var t = new Set([4,5,6]);

t.forEach(s.add, s);
console.log(s);   // 1,2,3,4,5,6

Und für eine Map können Sie Folgendes tun:

var s = new Map([["key1", 1], ["key2", 2]]);
var t = new Map([["key3", 3], ["key4", 4]]);

t.forEach(function(value, key) {
    s.set(key, value);
});
23
jfriend00

Bearbeiten:

Ich habe meine ursprüngliche Lösung mit anderen Lösungsvorschlägen hier verglichen und festgestellt, dass sie sehr ineffizient ist.

Der Benchmark selbst ist sehr interessant ( link ). Er vergleicht 3 Lösungen (höher ist besser):

  • @ bfred.its Lösung, die Werte nacheinander hinzufügt (14.955 op/sec)
  • @ jameslks Lösung, die einen selbstaufrufenden Generator verwendet (5,089 op/sec)
  • meine eigene, die die Verwendung von & Spread (3,434 op/sec) verwendet

Wie Sie sehen, ist die Lösung von @ bfred.it definitiv der Gewinner.

Leistung + Unveränderlichkeit

Vor diesem Hintergrund ist hier eine leicht modifizierte Version, die nicht mutiert den ursprünglichen Satz und gibt eine variable Anzahl von Iterablen an .__ aus. als Argumente kombinieren:

function union(...iterables) {
  const set = new Set();

  for (let iterable of iterables) {
    for (let item of iterable) {
      set.add(item);
    }
  }

  return set;
}

Verwendungszweck:

const a = new Set([1, 2, 3]);
const b = new Set([1, 3, 5]);
const c = new Set([4, 5, 6]);

union(a,b,c) // {1, 2, 3, 4, 5, 6}

Ursprüngliche Antwort

Ich möchte einen anderen Ansatz vorschlagen, der reduce und den Operator spread verwendet:

Implementierung

function union (sets) {
  return sets.reduce((combined, list) => {
    return new Set([...combined, ...list]);
  }, new Set());
}

Verwendungszweck:

const a = new Set([1, 2, 3]);
const b = new Set([1, 3, 5]);
const c = new Set([4, 5, 6]);

union([a, b, c]) // {1, 2, 3, 4, 5, 6}

Tipp:

Wir können auch den Operator rest verwenden, um die Schnittstelle ein bisschen schöner zu gestalten:

function union (...sets) {
  return sets.reduce((combined, list) => {
    return new Set([...combined, ...list]);
  }, new Set());
}

Anstatt ein array von Mengen zu übergeben, können wir eine beliebige Anzahl von arguments von Mengen übergeben:

union(a, b, c) // {1, 2, 3, 4, 5, 6}
11
Asaf Katz

Die bewährte Antwort ist großartig, aber jedes Mal wird ein neuer Satz erstellt.

Wenn Sie stattdessen ein vorhandenes Objekt mutieren möchten, verwenden Sie eine Hilfsfunktion.

Einstellen

function concatSets(set, ...iterables) {
    for (const iterable of iterables) {
        for (const item of iterable) {
            set.add(item);
        }
    }
}

Verwendungszweck:

const setA = new Set([1, 2, 3]);
const setB = new Set([4, 5, 6]);
const setC = new Set([7, 8, 9]);
concatSets(setA, setB, setC);
// setA will have items 1, 2, 3, 4, 5, 6, 7, 8, 9

Karte

function concatMaps(map, ...iterables) {
    for (const iterable of iterables) {
        for (const item of iterable) {
            map.set(...item);
        }
    }
}

Verwendungszweck:

const mapA = new Map().set('S', 1).set('P', 2);
const mapB = new Map().set('Q', 3).set('R', 4);
concatMaps(mapA, mapB);
// mapA will have items ['S', 1], ['P', 2], ['Q', 3], ['R', 4]
10
bfred.it

Um die Sets in den Array-Sets zusammenzuführen, können Sie dies tun

var Sets = [set1, set2, set3];

var merged = new Set([].concat(...Sets.map(set => Array.from(set))));

Es ist für mich etwas rätselhaft, warum das Folgende, das gleichwertig sein sollte, zumindest in Babel versagt:

var merged = new Set([].concat(...Sets.map(Array.from)));
4
user663031

Nein, es gibt keine integrierten Operationen für diese, aber Sie können sie ganz einfach selbst erstellen:

Map.prototype.assign = function(...maps) {
    for (const m of maps)
        for (const kv of m)
            this.add(...kv);
    return this;
};

Set.prototype.concat = function(...sets) {
    const c = this.constructor;
    let res = new (c[Symbol.species] || c)();
    for (const set of [this, ...sets])
        for (const v of set)
            res.add(v);
    return res;
};
1
Bergi

Basierend auf der Antwort von Asaf Katz, hier eine TypeScript-Version

export function union<T> (...iterables: Array<Set<T>>): Set<T> {
  const set = new Set<T>()
  iterables.forEach(iterable => {
    iterable.forEach(item => set.add(item))
  })
  return set
}
0
ndp

Beispiel

const mergedMaps = (...maps) => {
    const dataMap = new Map([])

    for (const map of maps) {
        for (const [key, value] of map) {
            dataMap.set(key, value)
        }
    }

    return dataMap
}

Verwendungszweck

const map = mergedMaps(new Map([[1, false]]), new Map([['foo', 'bar']]), new Map([['lat', 1241.173512]]))
Array.from(map.keys()) // [1, 'foo', 'lat']
0
dimpiax

Es ist ergibt keinen Sinn, new Set(...anArrayOrSet) aufzurufen, wenn mehrere Elemente (entweder aus einem Array oder einer anderen Menge) zu einer vorhandenen Menge hinzugefügt werden.

Ich benutze dies in einer reduce Funktion, und es ist einfach nur albern. Selbst wenn der Spread-Operator ...array verfügbar ist, sollten Sie ihn in diesem Fall nicht verwenden, da er Prozessor-, Speicher- und Zeitressourcen verschwendet.

// Add any Map or Set to another
function addAll(target, source) {
  if (target instanceof Map) {
    Array.from(source.entries()).forEach(it => target.set(it[0], it[1]))
  } else if (target instanceof Set) {
    source.forEach(it => target.add(it))
  }
}

Demo-Snippet

// Add any Map or Set to another
function addAll(target, source) {
  if (target instanceof Map) {
    Array.from(source.entries()).forEach(it => target.set(it[0], it[1]))
  } else if (target instanceof Set) {
    source.forEach(it => target.add(it))
  }
}

const items1 = ['a', 'b', 'c']
const items2 = ['a', 'b', 'c', 'd']
const items3 = ['d', 'e']

let set

set = new Set(items1)
addAll(set, items2)
addAll(set, items3)
console.log('adding array to set', Array.from(set))

set = new Set(items1)
addAll(set, new Set(items2))
addAll(set, new Set(items3))
console.log('adding set to set', Array.from(set))

const map1 = [
  ['a', 1],
  ['b', 2],
  ['c', 3]
]
const map2 = [
  ['a', 1],
  ['b', 2],
  ['c', 3],
  ['d', 4]
]
const map3 = [
  ['d', 4],
  ['e', 5]
]

const map = new Map(map1)
addAll(map, new Map(map2))
addAll(map, new Map(map3))
console.log('adding map to map',
  'keys', Array.from(map.keys()),
  'values', Array.from(map.values()))
0
Steven Spungin