ES6 मैप्स / सेट को मर्ज करने का सबसे सरल तरीका?


170

क्या ES6 मैप्स को एक साथ मर्ज करने का एक सरल तरीका है (जैसे Object.assign)? और जब हम इस पर हैं, तो ES6 सेट्स (जैसे Array.concat) के बारे में क्या ?


4
इस ब्लॉग पोस्ट में कुछ अंतर्दृष्टि है। 2ality.com/2015/01/es6-set-operations.html
lemieuxster

मैप के लिए AFAIK आपको उपयोग करने की आवश्यकता होगी for..ofक्योंकि keyकिसी भी प्रकार
पॉल एस।

जवाबों:


265

सेट के लिए:

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

नक्शे के लिए:

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

ध्यान दें कि यदि कई मानचित्रों में एक ही कुंजी है, तो विलय किए गए नक्शे का मूल्य उस कुंजी के साथ अंतिम विलय के नक्शे का मूल्य होगा।


4
दस्तावेज़ीकरण Map: "कंस्ट्रक्टर: new Map([iterable])", " iterableएक एरे या अन्य चलने योग्य वस्तु है जिसके तत्व कुंजी-मूल्य जोड़े (2-तत्व एरे) हैं। प्रत्येक कुंजी-मूल्य जोड़ी को नए मानचित्र में जोड़ा जाता है। " - सिर्फ एक संदर्भ के रूप में।
user4642212

34
बड़े सेटों के लिए, बस चेतावनी दी जाती है कि यह दोनों सेटों की सामग्री को दो बार पुन: प्रसारित करता है, एक बार एक अस्थायी सरणी बनाने के लिए जिसमें दो सेटों का मिलन होता है, फिर उस अस्थायी सरणी को सेट कंस्ट्रक्टर के पास भेजते हैं जहाँ यह नया सेट बनाने के लिए फिर से पुनरावृत्त होता है। ।
jfriend00

2
@ jfriend00: बेहतर तरीके के लिए नीचे दिए गए jameslk के उत्तर को देखें
Bergi

2
@torazaburo: जैसा कि jfriend00 ने कहा, ओरिएल्स समाधान अनावश्यक मध्यवर्ती सरणियों का निर्माण करता है। Mapकंस्ट्रक्टर को पुनरावृति पास करने से उनकी मेमोरी खपत से बचा जाता है।
बरगी

2
मुझे लगता है कि ES6 सेट / मानचित्र कुशल मर्ज तरीके प्रदान नहीं करते हैं।
एंडी

47

यहाँ जनरेटर का उपयोग कर मेरा समाधान है:

मानचित्रों के लिए:

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' ] ]

सेट के लिए:

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' ]

35
(आईआईजीएफई = तत्काल-इनवॉल्ड जेनरेटर फंक्शन एक्सप्रेशन)
ओरिओल

3
अच्छा भी m2.forEach((k,v)=>m1.set(k,v))अगर आप चाहते हैं आसान ब्राउज़र समर्थन
caub

9
@ अच्छा समाधान, लेकिन याद रखें कि forEach का पहला पैरामीटर मान है, इसलिए आपका फ़ंक्शन m2.forEach ((v, k) => m1.set (k, v)) होना चाहिए;
डेविड नोरेना

44

जिन कारणों से मुझे समझ में नहीं आता है, आप एक निर्मित ऑपरेशन के साथ एक सेट की सामग्री को सीधे दूसरे में नहीं जोड़ सकते। यूनियन, इंटरसेक्ट, मर्ज, आदि जैसे ऑपरेशन ... बहुत बुनियादी सेट ऑपरेशन हैं, लेकिन बिल्ट-इन नहीं हैं। सौभाग्य से, आप इन सभी का निर्माण आसानी से कर सकते हैं।

तो, एक मर्ज ऑपरेशन को लागू करने के लिए (एक सेट की सामग्री को दूसरे या एक मैप में दूसरे में विलय करना), आप इसे एक ही .forEach()लाइन के साथ कर सकते हैं :

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

और, आपके लिए Map, आप ऐसा कर सकते हैं:

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);
});

या, ES6 सिंटैक्स में:

t.forEach((value, key) => s.set(key, value));

FYI करें, यदि आप बिल्ट-इन Setऑब्जेक्ट का एक सरल उपवर्ग चाहते हैं जिसमें एक .merge()विधि शामिल है , तो आप इसका उपयोग कर सकते हैं:

// 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;

इसका मतलब यह है कि यह स्वयं फ़ाइल setex.js में है ताकि आप require()बिल्ट-इन सेट के स्थान पर नोड.जेएस और उपयोग कर सकें।


3
मुझे नहीं लगता new Set(s, t)। काम करता है। tपैरामीटर नजरअंदाज कर दिया है। इसके अलावा, यह स्पष्ट रूप से उचित व्यवहार नहीं है addकि इसके पैरामीटर के प्रकार का पता लगाया जाए और यदि कोई सेट सेट के तत्वों को जोड़ देता है, क्योंकि तब सेट में खुद को जोड़ने का कोई तरीका नहीं होगा।

@torazaburo - .add()एक सेट लेने की विधि के लिए , मैं आपकी बात समझता हूं। मुझे लगता है कि सेट के संयोजन का उपयोग करने में सक्षम होने की तुलना में मैं अभी तक कम उपयोग .add()करता हूं क्योंकि मुझे कभी सेट या सेट की आवश्यकता नहीं थी, लेकिन मुझे कई बार सेट को मर्ज करने की आवश्यकता है। बस एक व्यवहार बनाम दूसरे की उपयोगिता की राय।
jfriend00

अर्घ, मुझे नफरत है कि यह नक्शे के लिए काम नहीं करता है: n.forEach(m.add, m)- यह कुंजी / मूल्य जोड़े को उल्टा करता है!
बेर्गी

@ बरगी - हाँ, यह अजीब है Map.prototype.forEach()और Map.prototype.set()उलटे तर्क दिए हैं। किसी के द्वारा एक निरीक्षण की तरह लगता है। यह एक साथ उपयोग करने की कोशिश करते समय अधिक कोड को मजबूर करता है।
jfriend00

@ jfriend00: OTOH, यह थोड़े समझ में आता है। setकुंजी / मान युग्मों के लिए पैरामीटर ऑर्डर प्राकृतिक है, एस विधि (और वस्तुओं की तरह या जो चीजों को गणना करता है) के forEachसाथ गठबंधन किया गया है । ArrayforEach$.each_.each
बरगी

20

संपादित करें :

मैंने अन्य समाधानों के खिलाफ अपने मूल समाधान को यहां सुझाया और पाया कि यह बहुत अक्षम है।

बेंचमार्क अपने आप में बहुत दिलचस्प है ( लिंक ) यह 3 समाधानों की तुलना करता है (उच्चतर बेहतर है):

  • @ bfred.it का समाधान, जो एक-एक करके मूल्य जोड़ता है (14,955 सेशन / सेकंड)
  • @ jameslk का समाधान, जो एक स्व-आह्वान जनरेटर (5,089 सेशन / सेकंड) का उपयोग करता है
  • मेरा अपना, जो कम करता है और फैलता है (3,434 सेशन / सेकंड)

जैसा कि आप देख सकते हैं, @ bfred.it का समाधान निश्चित रूप से विजेता है।

प्रदर्शन + अपरिहार्यता

इसे ध्यान में रखते हुए, यहाँ थोड़ा संशोधित संस्करण है जो मूल सेट को परिवर्तित नहीं करता है और तर्कों के संयोजन के लिए पुनरावृत्तियों की एक चर संख्या को स्वीकार करता है:

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

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

  return set;
}

उपयोग:

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}

मूल उत्तर

मैं एक और दृष्टिकोण का उपयोग करना चाहते हैं, reduceऔर spreadऑपरेटर का उपयोग कर :

कार्यान्वयन

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

उपयोग:

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}

सुझाव:

हम restइंटरफ़ेस को थोड़ा अच्छा बनाने के लिए ऑपरेटर का उपयोग भी कर सकते हैं :

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

अब, के बजाय एक गुजरने वाली सरणी सेट की है, हम में से एक मनमाना संख्या पारित कर सकते हैं तर्क सेट की:

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

यह बहुत ही अयोग्य है।
बर्गी

1
हाय @Bergi, तुम सही हो। । मेरी जागरूकता (ऊपर उठाने के लिए धन्यवाद: मैं अपने समाधान परीक्षण किया है के खिलाफ दूसरों को यहाँ का सुझाव दिया और खुद के लिए यह साबित कर दिया इसके अलावा, मैं अपने जवाब यह है कि प्रतिबिंबित करने के लिए संपादित किया है कृपया अपने downvote को हटाने पर विचार
आसफ Katz

1
शानदार, प्रदर्शन की तुलना के लिए धन्यवाद। मजेदार यह है कि "असम्पीडित" समाधान सबसे तेज़ कैसे है?) एक सुधार पर देखने के लिए यहां आया forofऔर add, जो अभी बहुत अक्षम है। मैं वास्तव में addAll(iterable)सेट्स पर एक विधि की कामना करता
हूं

1
टाइपस्क्रिप्ट संस्करण: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 }
ndp

4
आपके उत्तर के शीर्ष के पास का jsperf लिंक बस्टेड लगता है। इसके अलावा, आप bfred के समाधान का उल्लेख करते हैं जो मुझे यहां कहीं भी दिखाई नहीं देता है।
jfriend00

12

स्वीकृत उत्तर बहुत अच्छा है लेकिन यह हर बार एक नया सेट बनाता है।

यदि आप इसके बजाय किसी मौजूदा ऑब्जेक्ट को म्यूट करना चाहते हैं , तो एक सहायक फ़ंक्शन का उपयोग करें।

सेट

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

उपयोग:

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);
        }
    }
}

उपयोग:

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]

5

सरणी सेट में सेट को मर्ज करने के लिए, आप कर सकते हैं

var Sets = [set1, set2, set3];

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

यह मेरे लिए थोड़ा रहस्यमय है कि निम्नलिखित क्यों, जो समकक्ष होना चाहिए, कम से कम बेबेल में विफल रहता है:

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

Array.fromअतिरिक्त पैरामीटर लेता है, दूसरा एक मैपिंग फ़ंक्शन है। Array.prototype.mapइसके कॉलबैक में तीन तर्क दिए गए हैं: (value, index, array)इसलिए यह प्रभावी रूप से कॉलिंग है Sets.map((set, index, array) => Array.from(set, index, array)। जाहिर है, indexएक संख्या है और एक मैपिंग फ़ंक्शन नहीं है इसलिए यह विफल हो जाता है।
निक

1

आसफ काटज़ के उत्तर के आधार पर, यहां एक टाइपस्क्रिप्ट संस्करण है:

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
}

1

यह किसी भी मतलब नहीं है कॉल करने के लिए new Set(...anArrayOrSet)जब कई तत्वों को जोड़ने (या तो किसी सरणी या एक और सेट से) एक मौजूदा सेट करने के लिए

मैं एक reduceफ़ंक्शन में इसका उपयोग करता हूं , और यह सिर्फ सादा मूर्खतापूर्ण है। यहां तक ​​कि अगर आपके पास ...arrayप्रसार ऑपरेटर उपलब्ध है, तो आपको इस मामले में इसका उपयोग नहीं करना चाहिए, क्योंकि यह प्रोसेसर, मेमोरी और समय संसाधनों को बर्बाद करता है।

// 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()))


1

सेट को सरणियों में बदलना, उन्हें समतल करना और अंत में निर्माणकर्ता uniqify करेगा।

const union = (...sets) => new Set(sets.map(s => [...s]).flat());

कृपया उत्तर के रूप में केवल कोड पोस्ट न करें, बल्कि यह भी बताएं कि आपका कोड क्या करता है और यह कैसे प्रश्न की समस्या को हल करता है। स्पष्टीकरण के साथ उत्तर आमतौर पर उच्च गुणवत्ता वाले होते हैं, और अपवोट्स को आकर्षित करने की अधिक संभावना होती है।
मार्क रोटेवेल 13

0

नहीं, इनके लिए कोई बिल्ट ऑपरेशन नहीं हैं, लेकिन आप इन्हें आसानी से अपना बना सकते हैं:

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;
};


2
उसे जो चाहिए वो करने दो।
पिशिश

1
अगर हर कोई चाहता है कि हम चाहते हैं कि हम एक और
स्मूश

0

उदाहरण

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']


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.