Czy istnieje prosty sposób na scalenie map ES6 razem (np. Object.assign
)? A skoro już o tym mowa, co z zestawami ES6 (takimi jak Array.concat
)?
for..of
ponieważ key
może być dowolnego typu
Czy istnieje prosty sposób na scalenie map ES6 razem (np. Object.assign
)? A skoro już o tym mowa, co z zestawami ES6 (takimi jak Array.concat
)?
for..of
ponieważ key
może być dowolnego typu
Odpowiedzi:
Do zestawów:
var merged = new Set([...set1, ...set2, ...set3])
W przypadku map:
var merged = new Map([...map1, ...map2, ...map3])
Zauważ, że jeśli wiele map ma ten sam klucz, wartość scalonej mapy będzie wartością ostatniej scalonej mapy z tym kluczem.
Map
: „Konstruktor: new Map([iterable])
”, „ iterable
to tablica lub inny iterowalny obiekt, którego elementy są parami klucz-wartość (tablice 2-elementowe). Każda para klucz-wartość jest dodawana do nowej mapy ”. - tylko jako odniesienie.
Map
konstruktora pozwala uniknąć zużycia pamięci.
Oto moje rozwiązanie wykorzystujące generatory:
Mapy:
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' ] ]
Zestawy:
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' ]
m2.forEach((k,v)=>m1.set(k,v))
jeśli chcesz mieć łatwą obsługę przeglądarki
Z powodów, których nie rozumiem, nie można bezpośrednio dodawać zawartości jednego zestawu do drugiego za pomocą wbudowanej operacji. Operacje takie jak sumowanie, przecinanie, scalanie itp. Są dość podstawowymi operacjami na zbiorach, ale nie są wbudowane. Na szczęście możesz dość łatwo zbudować je samodzielnie.
Tak więc, aby zaimplementować operację scalania (scalanie zawartości jednego zestawu w inny lub jednej mapy w inną), możesz to zrobić za pomocą jednej .forEach()
linii:
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
I na przykład Map
możesz to zrobić:
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);
});
Lub w składni ES6:
t.forEach((value, key) => s.set(key, value));
FYI, jeśli chcesz mieć prostą podklasę wbudowanego Set
obiektu, który zawiera .merge()
metodę, możesz użyć tego:
// subclass of Set that adds new methods
// Except where otherwise noted, arguments to methods
// can be a Set, anything derived from it or an Array
// Any method that returns a new Set returns whatever class the this object is
// allowing SetEx to be subclassed and these methods will return that subclass
// For this to work properly, subclasses must not change behavior of SetEx methods
//
// Note that if the contructor for SetEx is passed one or more iterables,
// it will iterate them and add the individual elements of those iterables to the Set
// If you want a Set itself added to the Set, then use the .add() method
// which remains unchanged from the original Set object. This way you have
// a choice about how you want to add things and can do it either way.
class SetEx extends Set {
// create a new SetEx populated with the contents of one or more iterables
constructor(...iterables) {
super();
this.merge(...iterables);
}
// merge the items from one or more iterables into this set
merge(...iterables) {
for (let iterable of iterables) {
for (let item of iterable) {
this.add(item);
}
}
return this;
}
// return new SetEx object that is union of all sets passed in with the current set
union(...sets) {
let newSet = new this.constructor(...sets);
newSet.merge(this);
return newSet;
}
// return a new SetEx that contains the items that are in both sets
intersect(target) {
let newSet = new this.constructor();
for (let item of this) {
if (target.has(item)) {
newSet.add(item);
}
}
return newSet;
}
// return a new SetEx that contains the items that are in this set, but not in target
// target must be a Set (or something that supports .has(item) such as a Map)
diff(target) {
let newSet = new this.constructor();
for (let item of this) {
if (!target.has(item)) {
newSet.add(item);
}
}
return newSet;
}
// target can be either a Set or an Array
// return boolean which indicates if target set contains exactly same elements as this
// target elements are iterated and checked for this.has(item)
sameItems(target) {
let tsize;
if ("size" in target) {
tsize = target.size;
} else if ("length" in target) {
tsize = target.length;
} else {
throw new TypeError("target must be an iterable like a Set with .size or .length");
}
if (tsize !== this.size) {
return false;
}
for (let item of target) {
if (!this.has(item)) {
return false;
}
}
return true;
}
}
module.exports = SetEx;
Ma to być w swoim własnym pliku setex.js, który możesz następnie require()
umieścić w node.js i użyć zamiast wbudowanego zestawu.
new Set(s, t)
. Pracuje. t
Parametr jest ignorowany. Ponadto oczywiście nie jest rozsądnym zachowaniem add
wykrywanie typu swojego parametru i dodanie do zestawu elementów zestawu, ponieważ wtedy nie byłoby sposobu, aby dodać sam zestaw do zestawu.
.add()
metody zrobienia zestawu, rozumiem twój punkt widzenia. Po prostu uważam, że jest to o wiele mniej przydatne niż możliwość łączenia zestawów za pomocą, .add()
ponieważ nigdy nie potrzebowałem zestawu ani zestawów, ale wielokrotnie musiałem łączyć zestawy. Tylko kwestia opinii o przydatności jednego zachowania do drugiego.
n.forEach(m.add, m)
- odwraca pary klucz / wartość!
Map.prototype.forEach()
i Map.prototype.set()
mam odwrócone argumenty. Wygląda na to, że ktoś przeoczył. Wymusza więcej kodu, gdy próbuje się ich używać razem.
set
kolejność parametrów jest naturalna dla par klucz / wartość, forEach
jest dopasowana do metody Array
s forEach
(i rzeczy takie jak $.each
lub, _.each
które również wyliczają obiekty).
Edycja :
Porównałem moje oryginalne rozwiązanie z innymi sugerowanymi tutaj rozwiązaniami i stwierdziłem, że jest bardzo nieefektywne.
Sam benchmark jest bardzo ciekawy ( link ) Porównuje 3 rozwiązania (wyższe tym lepsze):
- Rozwiązanie @ bfred.it, które dodaje wartości pojedynczo (14955 op / s)
- Rozwiązanie @ jameslk, które wykorzystuje samowywołujący się generator (5,089 op / s)
- mój własny, który używa redukcji i rozrzutu (3,434 op / s)
Jak widać, rozwiązanie @ bfred.it jest zdecydowanie zwycięzcą.
Wydajność + niezmienność
Mając to na uwadze, oto nieco zmodyfikowana wersja, która nie modyfikuje oryginalnego zestawu i wyklucza zmienną liczbę elementów iteracyjnych, które można połączyć jako argumenty:
function union(...iterables) { const set = new Set(); for (let iterable of iterables) { for (let item of iterable) { set.add(item); } } return set; }
Stosowanie:
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}
Chciałbym zasugerować inne podejście, użycie reduce
i spread
operatora:
function union (sets) {
return sets.reduce((combined, list) => {
return new Set([...combined, ...list]);
}, new Set());
}
Stosowanie:
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}
Wskazówka:
Możemy również skorzystać z rest
operatora, aby uczynić interfejs nieco ładniejszym:
function union (...sets) {
return sets.reduce((combined, list) => {
return new Set([...combined, ...list]);
}, new Set());
}
Teraz, zamiast przekazywać tablicę zbiorów, możemy przekazać dowolną liczbę argumentów zbiorów:
union(a, b, c) // {1, 2, 3, 4, 5, 6}
forof
i add
, które wydaje się po prostu bardzo nieefektywne. Naprawdę chciałbym mieć addAll(iterable)
metodę na
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 }
Zatwierdzona odpowiedź jest świetna, ale za każdym razem tworzy nowy zestaw.
Jeśli chcesz mutować zamiast tego istniejący obiekt, użyj funkcji pomocniczej.
function concatSets(set, ...iterables) {
for (const iterable of iterables) {
for (const item of iterable) {
set.add(item);
}
}
}
Stosowanie:
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
function concatMaps(map, ...iterables) {
for (const iterable of iterables) {
for (const item of iterable) {
map.set(...item);
}
}
}
Stosowanie:
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]
Aby scalić zestawy w zestawach szyku, możesz to zrobić
var Sets = [set1, set2, set3];
var merged = new Set([].concat(...Sets.map(set => Array.from(set))));
Jest dla mnie nieco tajemnicze, dlaczego poniższe, które powinny być równoważne, zawodzą przynajmniej w Babel:
var merged = new Set([].concat(...Sets.map(Array.from)));
Array.from
przyjmuje dodatkowe parametry, z których drugi to funkcja mapująca. Array.prototype.map
przekazuje trzy argumenty do swojego wywołania zwrotnego:, (value, index, array)
więc skutecznie wywołuje Sets.map((set, index, array) => Array.from(set, index, array)
. Oczywiście index
jest liczbą, a nie funkcją mapującą, więc zawodzi.
Nie ma sensu wywoływać new Set(...anArrayOrSet)
podczas dodawania wielu elementów (z tablicy lub innego zestawu) do istniejącego zestawu .
Używam tego w reduce
funkcji i jest to po prostu głupie. Nawet jeśli masz ...array
dostępny operator rozproszenia, nie powinieneś go używać w tym przypadku, ponieważ powoduje to marnowanie zasobów procesora, pamięci i czasu.
// 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))
}
}
// 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()))
Przekształć zestawy w tablice, spłaszcz je, a na koniec konstruktor ujednolici.
const union = (...sets) => new Set(sets.map(s => [...s]).flat());
Nie, nie ma dla nich wbudowanych operacji, ale możesz łatwo utworzyć własne:
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;
};
const mergedMaps = (...maps) => {
const dataMap = new Map([])
for (const map of maps) {
for (const [key, value] of map) {
dataMap.set(key, value)
}
}
return dataMap
}
const map = mergedMaps(new Map([[1, false]]), new Map([['foo', 'bar']]), new Map([['lat', 1241.173512]]))
Array.from(map.keys()) // [1, 'foo', 'lat']
Możesz użyć składni rozkładów, aby połączyć je ze sobą:
const map1 = {a: 1, b: 2}
const map2 = {b: 1, c: 2, a: 5}
const mergedMap = {...a, ...b}
=> {a: 5, b: 1, c: 2}
połącz z Mapą
let merge = {...map1,...map2};