उथले मर्ज के बजाय गहरा मर्ज कैसे करें?


339

Object.assign और Object स्प्रेड दोनों ही उथले मर्ज करते हैं।

समस्या का एक उदाहरण:

// No object nesting
const x = { a: 1 }
const y = { b: 1 }
const z = { ...x, ...y } // { a: 1, b: 1 }

आउटपुट वह है जिसकी आप अपेक्षा करेंगे। हालाँकि अगर मैं यह कोशिश करता हूँ:

// Object nesting
const x = { a: { a: 1 } }
const y = { a: { b: 1 } }
const z = { ...x, ...y } // { a: { b: 1 } }

के बजाय

{ a: { a: 1, b: 1 } }

आपको मिला

{ a: { b: 1 } }

x पूरी तरह से अधिलेखित है क्योंकि प्रसार सिंटैक्स केवल एक स्तर गहरा होता है। इसके साथ भी ऐसा ही है Object.assign()

क्या इसे करने का कोई तरीका है?


क्या एक वस्तु से दूसरी वस्तु में गुण की नकल करने के समान ही गहरा विलय होता है?

2
नहीं, जैसा कि ऑब्जेक्ट गुण को अधिलेखित नहीं किया जाना चाहिए, बल्कि प्रत्येक बाल वस्तु को लक्ष्य पर उसी बच्चे में विलय कर दिया जाना चाहिए यदि यह पहले से मौजूद है।
माइक

ES6 को अंतिम रूप दिया गया है और नई सुविधाओं को अब नहीं जोड़ा गया है, AFAIK।
कंगैक्स


1
@Oriol को jQuery की आवश्यकता होती है ...
m0meni

जवाबों:


331

क्या किसी को पता है कि ईएस 6 / ईएस 7 कल्पना में गहरी विलय मौजूद है?

नहीं, यह नहीं है।


21
कृपया संपादन इतिहास की समीक्षा करें। जिस समय मैंने इसका उत्तर दिया, सवाल यह था कि क्या कोई जानता है कि ES6 / ES7 कल्पना में गहरी विलय मौजूद है?

37
यह उत्तर अब इस प्रश्न पर लागू नहीं होता है - इसे अद्यतन या हटा दिया जाना चाहिए
DonVaughn

13
इस डिग्री के लिए प्रश्न को संपादित नहीं किया जाना चाहिए था। संपादन स्पष्ट करने के लिए हैं। एक नया प्रश्न पोस्ट किया जाना चाहिए था।
सीजे थॉम्पसन

171

मुझे पता है कि यह एक पुराना मुद्दा है, लेकिन ES2015 / ES6 में सबसे आसान समाधान मैं वास्तव में काफी सरल था, Object.assign () का उपयोग करके कर सकता हूं।

उम्मीद है कि यह मदद करता है:

/**
 * Simple object check.
 * @param item
 * @returns {boolean}
 */
export function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}

/**
 * Deep merge two objects.
 * @param target
 * @param ...sources
 */
export function mergeDeep(target, ...sources) {
  if (!sources.length) return target;
  const source = sources.shift();

  if (isObject(target) && isObject(source)) {
    for (const key in source) {
      if (isObject(source[key])) {
        if (!target[key]) Object.assign(target, { [key]: {} });
        mergeDeep(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    }
  }

  return mergeDeep(target, ...sources);
}

उदाहरण का उपयोग:

mergeDeep(this, { a: { b: { c: 123 } } });
// or
const merged = mergeDeep({a: 1}, { b : { c: { d: { e: 12345}}}});  
console.dir(merged); // { a: 1, b: { c: { d: [Object] } } }

आपको नीचे दिए गए उत्तर में इसका एक अपरिवर्तनीय संस्करण मिलेगा।

ध्यान दें कि इससे परिपत्र संदर्भों पर अनंत पुनरावृत्ति होगी। यदि आपको लगता है कि आप इस मुद्दे का सामना करेंगे, तो सर्कुलर संदर्भों का पता लगाने के तरीके के बारे में यहाँ कुछ शानदार जवाब हैं।


1
यदि आपके ऑब्जेक्ट ग्राफ में चक्र हैं, जो अनंत पुनरावृत्ति को जन्म देगा
the8472

item !== nullअंदर की जरूरत नहीं होनी चाहिए isObject, क्योंकि itemहालत की शुरुआत में
सत्यता के

2
यह क्यों लिखें: Object.assign(target, { [key]: {} })अगर यह बस हो सकता है target[key] = {}?
जार्ग लेहनी

1
... और target[key] = source[key]इसकी जगहObject.assign(target, { [key]: source[key] });
Jürg Lehni

3
यह किसी भी गैर-सादे वस्तुओं का समर्थन नहीं करता है target। उदाहरण के लिए, mergeDeep({a: 3}, {a: {b: 4}})एक संवर्धित Numberवस्तु का परिणाम होगा , जो स्पष्ट रूप से वांछित नहीं है। इसके अलावा, isObjectसरणियों को स्वीकार नहीं करता है, लेकिन किसी अन्य मूल वस्तु प्रकार को स्वीकार करता है, जैसे कि Date, जिसकी गहरी नकल नहीं की जानी चाहिए।
रीव

121

आप लोडश मर्ज का उपयोग कर सकते हैं :

var object = {
  'a': [{ 'b': 2 }, { 'd': 4 }]
};

var other = {
  'a': [{ 'c': 3 }, { 'e': 5 }]
};

_.merge(object, other);
// => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }

6
हे लोगों, यह सबसे सरल और सबसे सुंदर समाधान है। लोदाश कमाल का है, उन्हें इसे कोर जेएस ऑब्जेक्ट के रूप में शामिल करना चाहिए
नर्बोल एल्प्सबायेव

11
परिणाम नहीं होना चाहिए { 'a': [{ 'b': 2 }, { 'c': 3 }, { 'd': 4 }, { 'e': 5 }] }?
जे। हेस्टर

अच्छा प्रश्न। यह एक अलग सवाल हो सकता है या लॉडश अनुरक्षकों के लिए एक हो सकता है।
एंड्रयूहेंडर्सन

7
परिणाम { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }सही है, क्योंकि हम किसी सरणी के तत्वों को मिला रहे हैं। तत्व 0की object.aहै {b: 2}, तत्व 0की other.aहै {c: 3}। जब इन दोनों को विलय कर दिया जाता है क्योंकि उनके पास समान सरणी सूचकांक होता है, तो परिणाम होता है { 'b': 2, 'c': 3 }, जो 0नई वस्तु में तत्व है।
अलेक्जेंड्रू फरकुलिटा

मैं पसंद इस एक , यह 6x छोटे Gzipped है।
सोलो

101

समस्या गैर-तुच्छ है जब यह वस्तुओं की मेजबानी या किसी भी प्रकार की वस्तु की बात आती है जो मूल्यों के बैग से अधिक जटिल है

  • क्या आप मूल्य प्राप्त करने के लिए एक गेटर का आह्वान करते हैं या आप संपत्ति के विवरणक की नकल करते हैं?
  • क्या होगा यदि मर्ज लक्ष्य के पास एक सेटर है (या तो खुद की संपत्ति है या इसकी प्रोटोटाइप श्रृंखला में है)? क्या आप मूल्य को पहले से मौजूद मानते हैं या वर्तमान मूल्य को अपडेट करने के लिए सेटर को कॉल करते हैं?
  • क्या आप स्वयं की संपत्ति के कार्यों को लागू करते हैं या उन्हें कॉपी करते हैं? क्या होगा यदि वे उस समय अपने कार्यक्षेत्र श्रृंखला में किसी चीज़ के आधार पर फ़ंक्शंस या एरो फ़ंक्शंस को बाध्य करते हैं जो वे परिभाषित थे?
  • क्या होगा अगर यह एक डोम नोड की तरह कुछ है? आप निश्चित रूप से इसे सरल वस्तु के रूप में नहीं मानना ​​चाहते हैं और इसके सभी गुणों को गहराई से मर्ज कर रहे हैं
  • सरणियों या नक्शे या सेट जैसे "सरल" संरचनाओं से कैसे निपटें? उन पर पहले से ही विचार करें या उन्हें भी मर्ज करें?
  • गैर-एन्यूमरेबल स्वयं के गुणों से कैसे निपटें?
  • नए उपप्रकारों के बारे में क्या? बस संदर्भ या गहरे क्लोन द्वारा असाइन करें?
  • जमे हुए / सील / गैर-एक्स्टेंसिबल वस्तुओं से कैसे निपटें?

एक और बात ध्यान रखें: ऑब्जेक्ट ग्राफ़ जिनमें चक्र होते हैं। यह आमतौर पर मुश्किल से निपटने के लिए नहीं है - बस Setपहले से देखी गई स्रोत वस्तुओं का एक रखें - लेकिन अक्सर भूल गए।

आपको शायद एक गहरा-मर्ज फ़ंक्शन लिखना चाहिए जो केवल आदिम मूल्यों और सरल वस्तुओं की अपेक्षा करता है - अधिकांश उन प्रकारों पर जो संरचित क्लोन एल्गोरिथ्म को संभाल सकते हैं - मर्ज स्रोतों के रूप में। यदि यह किसी भी चीज़ का सामना करता है तो उसे फेंक दें या गहरी मर्जिंग के बजाय केवल संदर्भ द्वारा निर्दिष्ट न करें।

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



आप कई अच्छे मुद्दे उठाते हैं और मुझे आपकी सिफारिश पर अमल करते हुए अच्छा लगता। इसलिए मैंने नीचे एक बनाने की कोशिश की। क्या आप कृपया एक नज़र और टिप्पणी कर सकते हैं? stackoverflow.com/a/48579540/8122487
राफाएक्सएक्स

66

यहाँ @ सलाकर के उत्तर का एक अपरिवर्तनीय (इनपुट्स को संशोधित नहीं करता है) संस्करण है। उपयोगी यदि आप कार्यात्मक प्रोग्रामिंग प्रकार सामान कर रहे हैं।

export function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}

export default function mergeDeep(target, source) {
  let output = Object.assign({}, target);
  if (isObject(target) && isObject(source)) {
    Object.keys(source).forEach(key => {
      if (isObject(source[key])) {
        if (!(key in target))
          Object.assign(output, { [key]: source[key] });
        else
          output[key] = mergeDeep(target[key], source[key]);
      } else {
        Object.assign(output, { [key]: source[key] });
      }
    });
  }
  return output;
}

1
@torazaburo मेरे द्वारा isObject फ़ंक्शन के लिए पिछली पोस्ट देखें
सालकर

इसे अद्यतन किया। कुछ परीक्षण के बाद मुझे गहरी नेस्टेड वस्तुओं के साथ एक बग मिला
CpILL

3
इसकी गणना की गई संपत्ति का नाम है, पहले संपत्ति के नाम के मूल्य का उपयोग करेगा key, बाद में संपत्ति का नाम "कुंजी" बना देगा। देखें: es6-features.org/#ComputedPropertyNames
CpILL

2
में isObjectआप की जाँच की जरूरत नहीं है && item !== null, क्योंकि अंत में के साथ लाइन शुरू होता है item &&, नहीं?
पंचांग

2
यदि स्रोत ने बाल वस्तुओं को लक्ष्य से अधिक गहरा कर दिया है, तो वे वस्तुएं अभी भी उन्हीं मूल्यों का संदर्भ देंगी mergedDeep(मुझे लगता है)। उदाहरण const target = { a: 1 }; const source = { b: { c: 2 } }; const merged = mergeDeep(target, source); merged.b.c; // 2 source.b.c = 3; merged.b.c; // 3 के लिए यह एक मुद्दा है? यह इनपुट्स को म्यूट नहीं करता है, लेकिन इनपुट के भविष्य के किसी भी म्यूटेशन को आउटपुट म्यूट कर सकता है, और आउटपुट म्यूटिंग इनपुट के लिए इसके विपरीत w / म्यूटेशन कर सकता है। हालांकि, इसकी कीमत क्या है, हालांकि, रद्दा का R.merge()व्यवहार समान है।
जेम्स कॉंकलिंग 20

40

चूंकि यह मुद्दा अभी भी सक्रिय है, यहाँ एक और दृष्टिकोण है:

  • ES6 / 2015
  • अपरिवर्तनीय (मूल वस्तुओं को संशोधित नहीं करता है)
  • संभालती सरणियाँ (उन्हें संघटन)

/**
* Performs a deep merge of objects and returns new object. Does not modify
* objects (immutable) and merges arrays via concatenation.
*
* @param {...object} objects - Objects to merge
* @returns {object} New object with merged key/values
*/
function mergeDeep(...objects) {
  const isObject = obj => obj && typeof obj === 'object';
  
  return objects.reduce((prev, obj) => {
    Object.keys(obj).forEach(key => {
      const pVal = prev[key];
      const oVal = obj[key];
      
      if (Array.isArray(pVal) && Array.isArray(oVal)) {
        prev[key] = pVal.concat(...oVal);
      }
      else if (isObject(pVal) && isObject(oVal)) {
        prev[key] = mergeDeep(pVal, oVal);
      }
      else {
        prev[key] = oVal;
      }
    });
    
    return prev;
  }, {});
}

// Test objects
const obj1 = {
  a: 1,
  b: 1, 
  c: { x: 1, y: 1 },
  d: [ 1, 1 ]
}
const obj2 = {
  b: 2, 
  c: { y: 2, z: 2 },
  d: [ 2, 2 ],
  e: 2
}
const obj3 = mergeDeep(obj1, obj2);

// Out
console.log(obj3);


यह अच्छा है। हालाँकि जब हमारे पास बार-बार तत्वों के साथ सरणी होती है तो ये समवर्ती होते हैं (दोहराए गए तत्व होते हैं)। मैंने इसे एक पैरामीटर लेने के लिए अनुकूलित किया है (सारणी अद्वितीय: सत्य / असत्य)।
अंतरिक्ष यात्री

1
सरणियों अद्वितीय बनाने के लिए आप बदल सकते हैं prev[key] = pVal.concat(...oVal);करने के लिएprev[key] = [...pVal, ...oVal].filter((element, index, array) => array.indexOf(element) === index);
रिचर्ड Herries

1
इतना अच्छा और साफ !! निश्चित रूप से सबसे अच्छा जवाब यहाँ!
538ROMEO

शानदार। यह एक प्रदर्शित करता है कि सरणियों का विलय हो जाता है, जो कि मैं देख रहा था।
स्चलैकैका

हां, @CplLL समाधान को अपरिवर्तनीय कहा जाता है, लेकिन उपयोग reduce नहीं करते समय फ़ंक्शन के अंदर वास्तविक ऑब्जेक्ट म्यूटेबिलिटी का उपयोग करता है।
अगस्टिन रिडिंगर

30

मुझे पता है कि पहले से ही बहुत सारे उत्तर हैं और कई टिप्पणियों के अनुसार वे काम नहीं करेंगे। एकमात्र आम सहमति यह है कि यह इतना जटिल है कि किसी ने भी इसके लिए कोई मानक नहीं बनाया है । हालांकि, SO में स्वीकृत अधिकांश उत्तर "सरल ट्रिक्स" को उजागर करते हैं जो व्यापक रूप से उपयोग किए जाते हैं। इसलिए, मेरे जैसे हम सभी के लिए, जो कोई विशेषज्ञ नहीं हैं, लेकिन जावास्क्रिप्ट की जटिलता के बारे में थोड़ा और अधिक समझकर सुरक्षित कोड लिखना चाहते हैं, मैं कुछ प्रकाश डालने की कोशिश करूंगा।

हमारे हाथ गंदे होने से पहले, मुझे 2 बिंदु स्पष्ट करने दें:

  • [अस्वीकरण] मैं नीचे एक फ़ंक्शन का प्रस्ताव करता हूं जो यह बताता है कि हम प्रतिलिपि के लिए जावास्क्रिप्ट ऑब्जेक्ट में गहरी लूप कैसे बनाते हैं और यह दिखाता है कि आमतौर पर बहुत जल्द ही टिप्पणी की जाती है। यह उत्पादन-तैयार नहीं है। स्पष्टता के लिए, मैंने पर्सपेन्डली को अन्य विचार जैसे कि गोलाकार वस्तुओं (एक सेट या ट्रैक द्वारा अप्रकाशित प्रतीक संपत्ति) पर छोड़ दिया है , संदर्भ मूल्य या गहरी क्लोन , अपरिवर्तनीय गंतव्य ऑब्जेक्ट (गहरा क्लोन फिर से), केस-बाय-केस स्टडी को कॉपी करता है ? वस्तुओं के प्रत्येक प्रकार , के माध्यम से प्राप्त / सेट गुण accessors ... इसके अलावा, मैं परीक्षण के प्रदर्शन -although यह important- हैं, क्योंकि उस बिंदु यहाँ नहीं है या तो नहीं किया।
  • मैं मर्ज की बजाय कॉपी या असाइन की गई शर्तों का उपयोग करूंगा । क्योंकि मेरे दिमाग में एक मर्ज रूढ़िवादी है और संघर्षों पर विफल होना चाहिए। यहां, संघर्ष करते समय, हम चाहते हैं कि स्रोत गंतव्य को अधिलेखित कर दे। जैसा करता है।Object.assign

के साथ for..inया Object.keysभ्रामक उत्तर दे रहे हैं

एक गहरी प्रतिलिपि बनाना इतना बुनियादी और सामान्य अभ्यास लगता है कि हम एक-लाइनर या, कम से कम, एक साधारण जीत के माध्यम से एक त्वरित जीत की उम्मीद करते हैं। हमें उम्मीद नहीं है कि हमें एक पुस्तकालय की आवश्यकता है या 100 लाइनों का एक कस्टम फ़ंक्शन लिखना चाहिए।

जब मैं पहली बार पढ़ा Salakar का जवाब , मैं वास्तव में सोचा था कि मैं बेहतर कर सकते थे और सरल (इसे आपके साथ तुलना कर सकते हैं Object.assignपर x={a:1}, y={a:{b:1}})। तब मैंने the8472 का जवाब पढ़ा और मुझे लगा कि ... इतनी आसानी से दूर नहीं हो रहा है, पहले से दिए गए उत्तरों में सुधार हमें दूर नहीं मिलेगा।

आइए एक पल में गहरी नकल और पुनरावृत्ति होने दें। बस विचार करें कि कैसे (गलत तरीके से) लोग एक बहुत ही साधारण वस्तु को कॉपी करने के लिए गुणों को पार्स करते हैं।

const y = Object.create(
    { proto : 1 },
    { a: { enumerable: true, value: 1},
      [Symbol('b')] : { enumerable: true, value: 1} } )

Object.assign({},y)
> { 'a': 1, Symbol(b): 1 } // All (enumerable) properties are copied

((x,y) => Object.keys(y).reduce((acc,k) => Object.assign(acc, { [k]: y[k] }), x))({},y)
> { 'a': 1 } // Missing a property!

((x,y) => {for (let k in y) x[k]=y[k];return x})({},y)
> { 'a': 1, 'proto': 1 } // Missing a property! Prototype's property is copied too!

Object.keysअपने स्वयं के गैर-असंख्य गुणों, स्वयं के प्रतीक-कुंजी वाले गुणों और सभी प्रोटोटाइप गुणों को छोड़ देगा। यदि आपकी वस्तुओं में से कोई भी नहीं है तो यह ठीक हो सकता है। लेकिन यह ध्यान रखें कि Object.assignखुद के प्रतीक-रूपी असंख्य गुणों को संभालता है। तो आपकी कस्टम कॉपी ने अपना खिलना खो दिया।

for..inस्रोत के गुण प्रदान करेगा, इसके प्रोटोटाइप का और पूर्ण प्रोटोटाइप श्रृंखला के बिना आप इसे (या इसे जानना) चाहते हैं। आपका लक्ष्य बहुत अधिक गुणों के साथ समाप्त हो सकता है, प्रोटोटाइप गुण और स्वयं के गुणों को मिलाकर।

आप एक सामान्य प्रयोजन समारोह में लिख रहे हैं और आप उपयोग नहीं कर रहे Object.getOwnPropertyDescriptors, Object.getOwnPropertyNames, Object.getOwnPropertySymbolsया Object.getPrototypeOf, आप सबसे शायद यह गलत कर रहे हैं।

अपने कार्य को लिखने से पहले विचार करने योग्य बातें

सबसे पहले, सुनिश्चित करें कि आप समझते हैं कि एक जावास्क्रिप्ट ऑब्जेक्ट क्या है। जावास्क्रिप्ट में, एक ऑब्जेक्ट अपने गुणों से बना होता है और एक (माता-पिता) प्रोटोटाइप ऑब्जेक्ट होता है। बदले में प्रोटोटाइप ऑब्जेक्ट अपने गुणों और एक प्रोटोटाइप ऑब्जेक्ट से बना है। और इसी तरह, एक प्रोटोटाइप श्रृंखला को परिभाषित करना।

एक संपत्ति कुंजी ( stringया symbol) और डिस्क्रिप्टर ( valueया get/ setएक्सेसर, और जैसी विशेषताओं enumerable) की एक जोड़ी है ।

अंत में, कई प्रकार की वस्तुएं हैं । आप किसी ऑब्जेक्ट ऑब्जेक्ट को ऑब्जेक्ट दिनांक या ऑब्जेक्ट फ़ंक्शन से अलग तरीके से हैंडल करना चाह सकते हैं।

इसलिए, अपनी गहरी कॉपी लिखते हुए, आपको कम से कम उन सवालों के जवाब देने चाहिए:

  1. मैं गहरे (पुनरावर्ती देखने के लिए उचित) या फ्लैट पर क्या विचार करता हूं?
  2. मैं किन गुणों को कॉपी करना चाहता हूं? (enumerable / non-enumerable, string-keyed / symbol-keyed, own properties / prototyp's own properties, values ​​/ descriptors ...)

मेरे उदाहरण के लिए, मैं समझता हूं कि केवल object Objectएस गहरी हैं , क्योंकि अन्य निर्माणकर्ताओं द्वारा बनाई गई अन्य वस्तुएं इन-डेप्थ लुक के लिए उचित नहीं हो सकती हैं। इस SO से अनुकूलित ।

function toType(a) {
    // Get fine type (object, array, function, null, error, date ...)
    return ({}).toString.call(a).match(/([a-z]+)(:?\])/i)[1];
}

function isDeepObject(obj) {
    return "Object" === toType(obj);
}

और मुझे यह optionsचुनने के लिए एक ऑब्जेक्ट बनाया गया कि क्या कॉपी करना है (डेमो उद्देश्य के लिए)।

const options = {nonEnum:true, symbols:true, descriptors: true, proto:true};

प्रस्तावित कार्य

आप इस प्लंकर में इसका परीक्षण कर सकते हैं ।

function deepAssign(options) {
    return function deepAssignWithOptions (target, ...sources) {
        sources.forEach( (source) => {

            if (!isDeepObject(source) || !isDeepObject(target))
                return;

            // Copy source's own properties into target's own properties
            function copyProperty(property) {
                const descriptor = Object.getOwnPropertyDescriptor(source, property);
                //default: omit non-enumerable properties
                if (descriptor.enumerable || options.nonEnum) {
                    // Copy in-depth first
                    if (isDeepObject(source[property]) && isDeepObject(target[property]))
                        descriptor.value = deepAssign(options)(target[property], source[property]);
                    //default: omit descriptors
                    if (options.descriptors)
                        Object.defineProperty(target, property, descriptor); // shallow copy descriptor
                    else
                        target[property] = descriptor.value; // shallow copy value only
                }
            }

            // Copy string-keyed properties
            Object.getOwnPropertyNames(source).forEach(copyProperty);

            //default: omit symbol-keyed properties
            if (options.symbols)
                Object.getOwnPropertySymbols(source).forEach(copyProperty);

            //default: omit prototype's own properties
            if (options.proto)
                // Copy souce prototype's own properties into target prototype's own properties
                deepAssign(Object.assign({},options,{proto:false})) (// Prevent deeper copy of the prototype chain
                    Object.getPrototypeOf(target),
                    Object.getPrototypeOf(source)
                );

        });
        return target;
    }
}

इसका उपयोग इस तरह किया जा सकता है:

const x = { a: { a: 1 } },
      y = { a: { b: 1 } };
deepAssign(options)(x,y); // { a: { a: 1, b: 1 } }

13

मैं लॉश का उपयोग करता हूं:

import _ = require('lodash');
value = _.merge(value1, value2);

2
ध्यान दें कि मर्ज ऑब्जेक्ट को बदल देगा, यदि आप कुछ ऐसा चाहते हैं जो ऑब्जेक्ट को म्यूट नहीं करता है, तो _cloneDeep(value1).merge(value2)
geckos

3
@geckos आप _.merge ({}, value1, value2) कर सकते हैं
Spenhouet

10

यहाँ टाइपस्क्रिप्ट कार्यान्वयन है:

export const mergeObjects = <T extends object = object>(target: T, ...sources: T[]): T  => {
  if (!sources.length) {
    return target;
  }
  const source = sources.shift();
  if (source === undefined) {
    return target;
  }

  if (isMergebleObject(target) && isMergebleObject(source)) {
    Object.keys(source).forEach(function(key: string) {
      if (isMergebleObject(source[key])) {
        if (!target[key]) {
          target[key] = {};
        }
        mergeObjects(target[key], source[key]);
      } else {
        target[key] = source[key];
      }
    });
  }

  return mergeObjects(target, ...sources);
};

const isObject = (item: any): boolean => {
  return item !== null && typeof item === 'object';
};

const isMergebleObject = (item): boolean => {
  return isObject(item) && !Array.isArray(item);
};

और यूनिट टेस्ट:

describe('merge', () => {
  it('should merge Objects and all nested Ones', () => {
    const obj1 = { a: { a1: 'A1'}, c: 'C', d: {} };
    const obj2 = { a: { a2: 'A2'}, b: { b1: 'B1'}, d: null };
    const obj3 = { a: { a1: 'A1', a2: 'A2'}, b: { b1: 'B1'}, c: 'C', d: null};
    expect(mergeObjects({}, obj1, obj2)).toEqual(obj3);
  });
  it('should behave like Object.assign on the top level', () => {
    const obj1 = { a: { a1: 'A1'}, c: 'C'};
    const obj2 = { a: undefined, b: { b1: 'B1'}};
    expect(mergeObjects({}, obj1, obj2)).toEqual(Object.assign({}, obj1, obj2));
  });
  it('should not merge array values, just override', () => {
    const obj1 = {a: ['A', 'B']};
    const obj2 = {a: ['C'], b: ['D']};
    expect(mergeObjects({}, obj1, obj2)).toEqual({a: ['C'], b: ['D']});
  });
  it('typed merge', () => {
    expect(mergeObjects<TestPosition>(new TestPosition(0, 0), new TestPosition(1, 1)))
      .toEqual(new TestPosition(1, 1));
  });
});

class TestPosition {
  constructor(public x: number = 0, public y: number = 0) {/*empty*/}
}

9

यहां एक और ES6 समाधान है, वस्तुओं और सरणियों के साथ काम करता है।

function deepMerge(...sources) {
  let acc = {}
  for (const source of sources) {
    if (source instanceof Array) {
      if (!(acc instanceof Array)) {
        acc = []
      }
      acc = [...acc, ...source]
    } else if (source instanceof Object) {
      for (let [key, value] of Object.entries(source)) {
        if (value instanceof Object && key in acc) {
          value = deepMerge(acc[key], value)
        }
        acc = { ...acc, [key]: value }
      }
    }
  }
  return acc
}

3
क्या यह परीक्षण किया गया है और / या पुस्तकालय का हिस्सा है, अच्छा लग रहा है, लेकिन यह सुनिश्चित करना पसंद करेंगे कि यह कुछ हद तक साबित हो।

9

इस समस्या को हल करने के लिए डीपमरेज एनपीएम पैकेज सबसे व्यापक रूप से इस्तेमाल की जाने वाली लाइब्रेरी है: https://www.npmjs.com/package/peepmerge


8

मैं एक बहुत ही सरल ES5 विकल्प प्रस्तुत करना चाहता हूं। फ़ंक्शन को 2 पैरामीटर मिलते हैं - targetऔर sourceयह "ऑब्जेक्ट" प्रकार का होना चाहिए। Targetपरिणामी वस्तु होगी। Targetअपने सभी मूल गुणों को रखता है लेकिन उनके मूल्यों को संशोधित किया जा सकता है।

function deepMerge(target, source) {
if(typeof target !== 'object' || typeof source !== 'object') return false; // target or source or both ain't objects, merging doesn't make sense
for(var prop in source) {
  if(!source.hasOwnProperty(prop)) continue; // take into consideration only object's own properties.
  if(prop in target) { // handling merging of two properties with equal names
    if(typeof target[prop] !== 'object') {
      target[prop] = source[prop];
    } else {
      if(typeof source[prop] !== 'object') {
        target[prop] = source[prop];
      } else {
        if(target[prop].concat && source[prop].concat) { // two arrays get concatenated
          target[prop] = target[prop].concat(source[prop]);
        } else { // two objects get merged recursively
          target[prop] = deepMerge(target[prop], source[prop]); 
        } 
      }  
    }
  } else { // new properties get added to target
    target[prop] = source[prop]; 
  }
}
return target;
}

मामलों:

  • यदि संपत्ति targetनहीं है source,target तो उसे मिल जाता है;
  • अगर targetएक है sourceसंपत्ति और targetऔर sourceदोनों वस्तुओं (4 में से 3 मामले) नहीं हैं, targetकी संपत्ति ओवरराइड हो जाता है;
  • यदि targetकोई sourceसंपत्ति है और उनमें से दोनों वस्तुएं / सरणियाँ (1 शेष मामला) हैं, तो पुनरावृत्ति दो वस्तुओं (या दो सरणियों के मिलन) को विलय करने से होती है;

निम्नलिखित पर भी विचार करें :

  1. array + obj = array
  2. obj + array = obj
  3. obj + obj = obj (पुनरावर्ती विलय)
  4. सरणी + सरणी = सरणी (संक्षिप्त)

यह अनुमानित है, आदिम प्रकार के साथ-साथ सरणियों और वस्तुओं का समर्थन करता है। जैसा कि हम 2 ऑब्जेक्ट्स को मर्ज कर सकते हैं, मुझे लगता है कि हम कम फंक्शन के माध्यम से 2 से अधिक मर्ज कर सकते हैं ।

एक उदाहरण देखें (और यदि आप चाहें तो इसके साथ खेलें) :

var a = {
   "a_prop": 1,
   "arr_prop": [4, 5, 6],
   "obj": {
     "a_prop": {
       "t_prop": 'test'
     },
     "b_prop": 2
   }
};

var b = {
   "a_prop": 5,
   "arr_prop": [7, 8, 9],
   "b_prop": 15,
   "obj": {
     "a_prop": {
       "u_prop": false
     },
     "b_prop": {
        "s_prop": null
     }
   }
};

function deepMerge(target, source) {
    if(typeof target !== 'object' || typeof source !== 'object') return false;
    for(var prop in source) {
    if(!source.hasOwnProperty(prop)) continue;
      if(prop in target) {
        if(typeof target[prop] !== 'object') {
          target[prop] = source[prop];
        } else {
          if(typeof source[prop] !== 'object') {
            target[prop] = source[prop];
          } else {
            if(target[prop].concat && source[prop].concat) {
              target[prop] = target[prop].concat(source[prop]);
            } else {
              target[prop] = deepMerge(target[prop], source[prop]); 
            } 
          }  
        }
      } else {
        target[prop] = source[prop]; 
      }
    }
  return target;
}

console.log(deepMerge(a, b));

एक सीमा है - ब्राउज़र की कॉल स्टैक लंबाई। आधुनिक ब्राउज़र कुछ वास्तविक स्तर पर पुनरावृत्ति (हजारों नेस्टेड कॉल के बारे में सोचते हैं) में एक त्रुटि फेंक देंगे। इसके अलावा, आप नई स्थितियों और प्रकार की जाँचों को जोड़कर अपनी इच्छानुसार सरणी + ऑब्जेक्ट आदि की स्थितियों का इलाज करने के लिए स्वतंत्र हैं।



7

क्या इसे करने का कोई तरीका है?

यदि npm पुस्तकालयों को एक समाधान के रूप में इस्तेमाल किया जा सकता है, तो आपके पास से ऑब्जेक्ट-मर्ज-एडवांस्ड वास्तव में ऑब्जेक्ट को मर्ज करने की अनुमति देता है और एक परिचित कॉलबैक फ़ंक्शन का उपयोग करके हर एक मर्ज कार्रवाई को अनुकूलित / ओवरराइड करता है। इसका मुख्य विचार सिर्फ गहरी विलय से अधिक है - जब दो चाबियाँ समान होती हैं तो मूल्य के साथ क्या होता है ? इस लाइब्रेरी में इस बात का ध्यान रखा जाता है - जब दो कुंजियाँ टकराती हैं, object-merge-advancedतो प्रकार का वज़न होता है, जो लक्ष्य होता है कि विलय के बाद जितना संभव हो उतना डेटा बनाए रखें:

ऑब्जेक्ट कुंजी विलय जितना संभव हो उतना डेटा बनाए रखने के लिए कुंजी मूल्य प्रकारों का विलय

पहले इनपुट तर्क की कुंजी # 1, दूसरी तर्क - # 2 चिह्नित है। प्रत्येक प्रकार के आधार पर, परिणाम कुंजी के मूल्य के लिए एक को चुना जाता है। आरेख में, "एक वस्तु" का मतलब एक सादे वस्तु है (सरणी आदि नहीं)।

जब चाबियाँ नहीं टकराती हैं, तो वे सभी परिणाम दर्ज करते हैं।

अपने उदाहरण स्निपेट से, यदि आप object-merge-advancedअपना कोड स्निपेट मर्ज करते थे:

const mergeObj = require("object-merge-advanced");
const x = { a: { a: 1 } };
const y = { a: { b: 1 } };
const res = console.log(mergeObj(x, y));
// => res = {
//      a: {
//        a: 1,
//        b: 1
//      }
//    }

यह एल्गोरिदम पुनरावर्ती रूप से सभी इनपुट ऑब्जेक्ट कुंजियों का पता लगाता है, तुलना करता है और नए विलय परिणाम देता है।


6

निम्न फ़ंक्शन ऑब्जेक्ट की एक गहरी प्रतिलिपि बनाता है, यह प्रतिलिपि बनाता है आदिम, सरणियों के साथ ही ऑब्जेक्ट

 function mergeDeep (target, source)  {
    if (typeof target == "object" && typeof source == "object") {
        for (const key in source) {
            if (source[key] === null && (target[key] === undefined || target[key] === null)) {
                target[key] = null;
            } else if (source[key] instanceof Array) {
                if (!target[key]) target[key] = [];
                //concatenate arrays
                target[key] = target[key].concat(source[key]);
            } else if (typeof source[key] == "object") {
                if (!target[key]) target[key] = {};
                this.mergeDeep(target[key], source[key]);
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}

6

ES5 के साथ एक सरल समाधान (मौजूदा मूल्य को अधिलेखित करें):

function merge(current, update) {
  Object.keys(update).forEach(function(key) {
    // if update[key] exist, and it's not a string or array,
    // we go in one level deeper
    if (current.hasOwnProperty(key) 
        && typeof current[key] === 'object'
        && !(current[key] instanceof Array)) {
      merge(current[key], update[key]);

    // if update[key] doesn't exist in current, or it's a string
    // or array, then assign/overwrite current[key] to update[key]
    } else {
      current[key] = update[key];
    }
  });
  return current;
}

var x = { a: { a: 1 } }
var y = { a: { b: 1 } }

console.log(merge(x, y));


बस मुझे क्या चाहिए - es6 निर्माण में समस्याएं पैदा कर रहा था - यह es5 विकल्प बम है
danday74

5

यहां अधिकांश उदाहरण बहुत जटिल लगते हैं, मैं अपने द्वारा बनाए गए टाइपस्क्रिप्ट में एक का उपयोग कर रहा हूं, मुझे लगता है कि इसे अधिकांश मामलों को कवर करना चाहिए (मैं नियमित डेटा के रूप में सरणियों को संभाल रहा हूं, बस उन्हें बदल रहा हूं)।

const isObject = (item: any) => typeof item === 'object' && !Array.isArray(item);

export const merge = <A = Object, B = Object>(target: A, source: B): A & B => {
  const isDeep = (prop: string) =>
    isObject(source[prop]) && target.hasOwnProperty(prop) && isObject(target[prop]);
  const replaced = Object.getOwnPropertyNames(source)
    .map(prop => ({ [prop]: isDeep(prop) ? merge(target[prop], source[prop]) : source[prop] }))
    .reduce((a, b) => ({ ...a, ...b }), {});

  return {
    ...(target as Object),
    ...(replaced as Object)
  } as A & B;
};

सादे जेएस में एक ही बात, बस मामले में:

const isObject = item => typeof item === 'object' && !Array.isArray(item);

const merge = (target, source) => {
  const isDeep = prop => 
    isObject(source[prop]) && target.hasOwnProperty(prop) && isObject(target[prop]);
  const replaced = Object.getOwnPropertyNames(source)
    .map(prop => ({ [prop]: isDeep(prop) ? merge(target[prop], source[prop]) : source[prop] }))
    .reduce((a, b) => ({ ...a, ...b }), {});

  return {
    ...target,
    ...replaced
  };
};

यहाँ मेरे परीक्षण के मामले हैं जो यह दिखाते हैं कि आप इसका उपयोग कैसे कर सकते हैं

describe('merge', () => {
  context('shallow merges', () => {
    it('merges objects', () => {
      const a = { a: 'discard' };
      const b = { a: 'test' };
      expect(merge(a, b)).to.deep.equal({ a: 'test' });
    });
    it('extends objects', () => {
      const a = { a: 'test' };
      const b = { b: 'test' };
      expect(merge(a, b)).to.deep.equal({ a: 'test', b: 'test' });
    });
    it('extends a property with an object', () => {
      const a = { a: 'test' };
      const b = { b: { c: 'test' } };
      expect(merge(a, b)).to.deep.equal({ a: 'test', b: { c: 'test' } });
    });
    it('replaces a property with an object', () => {
      const a = { b: 'whatever', a: 'test' };
      const b = { b: { c: 'test' } };
      expect(merge(a, b)).to.deep.equal({ a: 'test', b: { c: 'test' } });
    });
  });

  context('deep merges', () => {
    it('merges objects', () => {
      const a = { test: { a: 'discard', b: 'test' }  };
      const b = { test: { a: 'test' } } ;
      expect(merge(a, b)).to.deep.equal({ test: { a: 'test', b: 'test' } });
    });
    it('extends objects', () => {
      const a = { test: { a: 'test' } };
      const b = { test: { b: 'test' } };
      expect(merge(a, b)).to.deep.equal({ test: { a: 'test', b: 'test' } });
    });
    it('extends a property with an object', () => {
      const a = { test: { a: 'test' } };
      const b = { test: { b: { c: 'test' } } };
      expect(merge(a, b)).to.deep.equal({ test: { a: 'test', b: { c: 'test' } } });
    });
    it('replaces a property with an object', () => {
      const a = { test: { b: 'whatever', a: 'test' } };
      const b = { test: { b: { c: 'test' } } };
      expect(merge(a, b)).to.deep.equal({ test: { a: 'test', b: { c: 'test' } } });
    });
  });
});

कृपया मुझे बताएं कि क्या आपको लगता है कि मुझे कुछ कार्यक्षमता याद आ रही है।


5

यदि आप एक विशाल पुस्तकालय की आवश्यकता के बिना एक लाइनर रखना चाहते हैं, तो मैं आपको डीमेरज का उपयोग करने का सुझाव देता हूं । ( npm install deepmerge)

फिर, आप कर सकते हैं

deepmerge({ a: 1, b: 2, c: 3 }, { a: 2, d: 3 });

लेना

{ a: 2, b: 2, c: 3, d: 3 }

अच्छी बात यह है कि यह टाइपस्क्रिप्ट के लिए टाइपिंग के साथ तुरंत आता है। यह सरणियों को मर्ज करने की भी अनुमति देता है । एक वास्तविक ऑल-राउंडर समाधान यह है।


4

गहरी मर्जिंग के लिए हम $ .extend (true, object1, object2) का उपयोग कर सकते हैं । मान सत्य ब्योरा दो वस्तुओं को पुनरावर्ती रूप से मर्ज करता है, पहले को संशोधित करता है।

$ का विस्तार (सच है, लक्ष्य, वस्तु)


9
पूछने वाले ने कभी संकेत नहीं दिया कि वे jquery का उपयोग कर रहे हैं और एक देशी जावास्क्रिप्ट समाधान के लिए पूछ रहे हैं।
तेह जोए

यह ऐसा करने का एक बहुत ही सरल तरीका है और यह काम करता है। एक व्यवहार्य समाधान है कि मैं इस सवाल पूछ रहा था अगर मैं विचार करेंगे। :)
काशीराजा

यह एक बहुत अच्छा जवाब है लेकिन स्रोत कोड के लिए jQuery के लिए एक लिंक याद आ रही है। jQuery के पास परियोजना पर काम करने वाले बहुत सारे लोग हैं और उन्होंने कुछ समय गहरी नकल करने में बिताया है जो ठीक से काम कर रहा है। इसके अलावा, स्रोत कोड काफी "सरल" है: github.com/jquery/jquery/blob/master/src/core.js#L125 "सरल" उद्धरणों में है क्योंकि यह खुदाई करते समय जटिल होने लगता है jQuery.isPlainObject()। यह निर्धारित करने की जटिलता को उजागर करता है कि क्या कोई वस्तु एक सादा वस्तु है या नहीं, जो यहां दिए गए अधिकांश उत्तर एक लंबे शॉट द्वारा याद करते हैं। लगता है कि jQuery किस भाषा में लिखा गया है?
क्यूबिकलसॉफ्ट

4

यहां सीधे आगे, सरल समाधान जो Object.assignसिर्फ डीप की तरह काम करता है , और बिना किसी संशोधन के एक सरणी के लिए काम करता है

function deepAssign(target, ...sources) {
    for( source of sources){
        for(let k in source){
            let vs = source[k], vt = target[k];
            if(Object(vs)== vs && Object(vt)===vt ){
                target[k] = deepAssign(vt, vs)
                continue;
            }
            target[k] = source[k];
        }    
    }
    return target;
}

उदाहरण

x = { a: { a: 1 }, b:[1,2] };
y = { a: { b: 1 }, b:[3] };
z = {c:3,b:[,,,4]}
x = deepAssign(x,y,z)
// x will be
x ==  {
  "a": {
    "a": 1,
    "b": 1
  },
  "b": [    1,    2,    null,    4  ],
  "c": 3
}


3

कैश्ड रेडक्स स्टेट लोड करते समय मुझे यह समस्या आ रही थी। यदि मैं अभी कैश की गई स्थिति को लोड करता हूं, तो मैं एक अद्यतन राज्य संरचना के साथ नए ऐप संस्करण के लिए त्रुटियों में भागूंगा।

यह पहले ही उल्लेख किया गया था, कि लॉश फंक्शन प्रदान करता mergeहै, जिसका मैंने उपयोग किया था:

const currentInitialState = configureState().getState();
const mergedState = _.merge({}, currentInitialState, cachedState);
const store = configureState(mergedState);

3

कई उत्तर कोड की दसियों पंक्तियों का उपयोग करते हैं, या परियोजना में एक नई लाइब्रेरी जोड़ने की आवश्यकता होती है, लेकिन यदि आप पुनरावृत्ति का उपयोग करते हैं, तो यह कोड की सिर्फ 4 पंक्तियाँ हैं।

function merge(current, updates) {
  for (key of Object.keys(updates)) {
    if (!current.hasOwnProperty(key) || typeof updates[key] !== 'object') current[key] = updates[key];
    else merge(current[key], updates[key]);
  }
  return current;
}
console.log(merge({ a: { a: 1 } }, { a: { b: 1 } }));

हैंडलिंग संभालता है: उपरोक्त संस्करण नए लोगों के साथ पुराने सरणी मानों को अधिलेखित करता है। यदि आप चाहते हैं कि यह पुराने एरे वैल्यूज़ को बनाए रखे और नए जोड़े, तो केवल स्टैमैटमेंट के else if (current[key] instanceof Array && updates[key] instanceof Array) current[key] = current[key].concat(updates[key])ऊपर एक ब्लॉक जोड़ें elseऔर आप सभी सेट हो जाएँ।


1
मुझे यह पसंद है लेकिन इसे 'चालू' के लिए एक साधारण अपरिभाषित चेक की आवश्यकता है वरना {foo: undefined} का विलय नहीं होता है। बस एक (वर्तमान) लूप के लिए पहले जोड़ें।
एंड्रियास परडेइक

सुझाव के लिए धन्यवाद
विन्सेन्ट

2

यहाँ मैंने एक और लिखा है जो सरणियों का समर्थन करता है। यह उन्हें सहमति देता है।

function isObject(obj) {
    return obj !== null && typeof obj === 'object';
}


function isPlainObject(obj) {
    return isObject(obj) && (
        obj.constructor === Object  // obj = {}
        || obj.constructor === undefined // obj = Object.create(null)
    );
}

function mergeDeep(target, ...sources) {
    if (!sources.length) return target;
    const source = sources.shift();

    if(Array.isArray(target)) {
        if(Array.isArray(source)) {
            target.push(...source);
        } else {
            target.push(source);
        }
    } else if(isPlainObject(target)) {
        if(isPlainObject(source)) {
            for(let key of Object.keys(source)) {
                if(!target[key]) {
                    target[key] = source[key];
                } else {
                    mergeDeep(target[key], source[key]);
                }
            }
        } else {
            throw new Error(`Cannot merge object with non-object`);
        }
    } else {
        target = source;
    }

    return mergeDeep(target, ...sources);
};

2

इस फ़ंक्शन का उपयोग करें:

merge(target, source, mutable = false) {
        const newObj = typeof target == 'object' ? (mutable ? target : Object.assign({}, target)) : {};
        for (const prop in source) {
            if (target[prop] == null || typeof target[prop] === 'undefined') {
                newObj[prop] = source[prop];
            } else if (Array.isArray(target[prop])) {
                newObj[prop] = source[prop] || target[prop];
            } else if (target[prop] instanceof RegExp) {
                newObj[prop] = source[prop] || target[prop];
            } else {
                newObj[prop] = typeof source[prop] === 'object' ? this.merge(target[prop], source[prop]) : source[prop];
            }
        }
        return newObj;
    }

2

रामाडा जो जावास्क्रिप्ट फ़ंक्शंस की एक अच्छी लाइब्रेरी है, में मर्जडिप्लिप्ट और मर्जडिपरेइट है। इस समस्या के लिए इनमें से कोई भी बहुत अच्छा काम करता है। कृपया यहाँ प्रलेखन पर एक नज़र डालें: https://ramdajs.com/docs/#mergeDeepLeft

प्रश्न में विशिष्ट उदाहरण के लिए हम उपयोग कर सकते हैं:

import { mergeDeepLeft } from 'ramda'
const x = { a: { a: 1 } }
const y = { a: { b: 1 } }
const z = mergeDeepLeft(x, y)) // {"a":{"a":1,"b":1}}

2
// copies all properties from source object to dest object recursively
export function recursivelyMoveProperties(source, dest) {
  for (const prop in source) {
    if (!source.hasOwnProperty(prop)) {
      continue;
    }

    if (source[prop] === null) {
      // property is null
      dest[prop] = source[prop];
      continue;
    }

    if (typeof source[prop] === 'object') {
      // if property is object let's dive into in
      if (Array.isArray(source[prop])) {
        dest[prop] = [];
      } else {
        if (!dest.hasOwnProperty(prop)
        || typeof dest[prop] !== 'object'
        || dest[prop] === null || Array.isArray(dest[prop])
        || !Object.keys(dest[prop]).length) {
          dest[prop] = {};
        }
      }
      recursivelyMoveProperties(source[prop], dest[prop]);
      continue;
    }

    // property is simple type: string, number, e.t.c
    dest[prop] = source[prop];
  }
  return dest;
}

अध्याय परीक्षा:

describe('recursivelyMoveProperties', () => {
    it('should copy properties correctly', () => {
      const source: any = {
        propS1: 'str1',
        propS2: 'str2',
        propN1: 1,
        propN2: 2,
        propA1: [1, 2, 3],
        propA2: [],
        propB1: true,
        propB2: false,
        propU1: null,
        propU2: null,
        propD1: undefined,
        propD2: undefined,
        propO1: {
          subS1: 'sub11',
          subS2: 'sub12',
          subN1: 11,
          subN2: 12,
          subA1: [11, 12, 13],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
        propO2: {
          subS1: 'sub21',
          subS2: 'sub22',
          subN1: 21,
          subN2: 22,
          subA1: [21, 22, 23],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
      };
      let dest: any = {
        propS2: 'str2',
        propS3: 'str3',
        propN2: -2,
        propN3: 3,
        propA2: [2, 2],
        propA3: [3, 2, 1],
        propB2: true,
        propB3: false,
        propU2: 'not null',
        propU3: null,
        propD2: 'defined',
        propD3: undefined,
        propO2: {
          subS2: 'inv22',
          subS3: 'sub23',
          subN2: -22,
          subN3: 23,
          subA2: [5, 5, 5],
          subA3: [31, 32, 33],
          subB2: false,
          subB3: true,
          subU2: 'not null --- ',
          subU3: null,
          subD2: ' not undefined ----',
          subD3: undefined,
        },
        propO3: {
          subS1: 'sub31',
          subS2: 'sub32',
          subN1: 31,
          subN2: 32,
          subA1: [31, 32, 33],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
      };
      dest = recursivelyMoveProperties(source, dest);

      expect(dest).toEqual({
        propS1: 'str1',
        propS2: 'str2',
        propS3: 'str3',
        propN1: 1,
        propN2: 2,
        propN3: 3,
        propA1: [1, 2, 3],
        propA2: [],
        propA3: [3, 2, 1],
        propB1: true,
        propB2: false,
        propB3: false,
        propU1: null,
        propU2: null,
        propU3: null,
        propD1: undefined,
        propD2: undefined,
        propD3: undefined,
        propO1: {
          subS1: 'sub11',
          subS2: 'sub12',
          subN1: 11,
          subN2: 12,
          subA1: [11, 12, 13],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
        propO2: {
          subS1: 'sub21',
          subS2: 'sub22',
          subS3: 'sub23',
          subN1: 21,
          subN2: 22,
          subN3: 23,
          subA1: [21, 22, 23],
          subA2: [],
          subA3: [31, 32, 33],
          subB1: false,
          subB2: true,
          subB3: true,
          subU1: null,
          subU2: null,
          subU3: null,
          subD1: undefined,
          subD2: undefined,
          subD3: undefined,
        },
        propO3: {
          subS1: 'sub31',
          subS2: 'sub32',
          subN1: 31,
          subN2: 32,
          subA1: [31, 32, 33],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
      });
    });
  });

2

मुझे जावास्क्रिप्ट में गहरा मर्ज पाने के लिए केवल 2 लाइन समाधान मिला। मुझे पता है कि यह आपके लिए कैसे काम करता है।

const obj1 = { a: { b: "c", x: "y" } }
const obj2 = { a: { b: "d", e: "f" } }
temp = Object.assign({}, obj1, obj2)
Object.keys(temp).forEach(key => {
    temp[key] = (typeof temp[key] === 'object') ? Object.assign(temp[key], obj1[key], obj2[key]) : temp[key])
}
console.log(temp)

टेम्प ऑब्जेक्ट {a: {b: 'd', e: 'f', x: 'y'} प्रिंट करेगा


1
यह वास्तविक गहरा मर्ज नहीं करता है। इसके साथ असफल होगा merge({x:{y:{z:1}}}, {x:{y:{w:2}}})। Il भी obj1 में मौजूदा मानों को अपडेट करने में विफल रहेगा, यदि obj2 उनके पास भी है, उदाहरण के लिए merge({x:{y:1}}, {x:{y:2}})
Oreilles

1

कभी-कभी आपको गहरे मर्ज की आवश्यकता नहीं होती है, भले ही आप ऐसा सोचते हों। उदाहरण के लिए, यदि आपके पास नेस्टेड ऑब्जेक्ट्स के साथ एक डिफ़ॉल्ट कॉन्फिग है और आप इसे अपने स्वयं के कॉन्फिग के साथ गहराई से विस्तारित करना चाहते हैं, तो आप उसके लिए एक क्लास बना सकते हैं। अवधारणा बहुत सरल है:

function AjaxConfig(config) {

  // Default values + config

  Object.assign(this, {
    method: 'POST',
    contentType: 'text/plain'
  }, config);

  // Default values in nested objects

  this.headers = Object.assign({}, this.headers, { 
    'X-Requested-With': 'custom'
  });
}

// Define your config

var config = {
  url: 'https://google.com',
  headers: {
    'x-client-data': 'CI22yQEI'
  }
};

// Extend the default values with your own
var fullMergedConfig = new AjaxConfig(config);

// View in DevTools
console.log(fullMergedConfig);

आप इसे फंक्शन में कन्वर्ट कर सकते हैं (कंस्ट्रक्टर नहीं)।


1

यह एक सस्ता गहरा मर्ज है जो जितना सोच सकता है उतना कम कोड का उपयोग करता है। प्रत्येक स्रोत पिछली संपत्ति को अधिलेखित कर देता है जब यह मौजूद होता है।

const { keys } = Object;

const isObject = a => typeof a === "object" && !Array.isArray(a);
const merge = (a, b) =>
  isObject(a) && isObject(b)
    ? deepMerge(a, b)
    : isObject(a) && !isObject(b)
    ? a
    : b;

const coalesceByKey = source => (acc, key) =>
  (acc[key] && source[key]
    ? (acc[key] = merge(acc[key], source[key]))
    : (acc[key] = source[key])) && acc;

/**
 * Merge all sources into the target
 * overwriting primitive values in the the accumulated target as we go (if they already exist)
 * @param {*} target
 * @param  {...any} sources
 */
const deepMerge = (target, ...sources) =>
  sources.reduce(
    (acc, source) => keys(source).reduce(coalesceByKey(source), acc),
    target
  );

console.log(deepMerge({ a: 1 }, { a: 2 }));
console.log(deepMerge({ a: 1 }, { a: { b: 2 } }));
console.log(deepMerge({ a: { b: 2 } }, { a: 1 }));

1

मैं डीप मर्जिंग ऑब्जेक्ट्स के लिए निम्न लघु फ़ंक्शन का उपयोग कर रहा हूं।
यह मेरे लिए बहुत अच्छा काम करता है।
लेखक पूरी तरह से बताता है कि यह यहां कैसे काम करता है।

/*!
 * Merge two or more objects together.
 * (c) 2017 Chris Ferdinandi, MIT License, https://gomakethings.com
 * @param   {Boolean}  deep     If true, do a deep (or recursive) merge [optional]
 * @param   {Object}   objects  The objects to merge together
 * @returns {Object}            Merged values of defaults and options
 * 
 * Use the function as follows:
 * let shallowMerge = extend(obj1, obj2);
 * let deepMerge = extend(true, obj1, obj2)
 */

var extend = function () {

    // Variables
    var extended = {};
    var deep = false;
    var i = 0;

    // Check if a deep merge
    if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) {
        deep = arguments[0];
        i++;
    }

    // Merge the object into the extended object
    var merge = function (obj) {
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                // If property is an object, merge properties
                if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') {
                    extended[prop] = extend(extended[prop], obj[prop]);
                } else {
                    extended[prop] = obj[prop];
                }
            }
        }
    };

    // Loop through each object and conduct a merge
    for (; i < arguments.length; i++) {
        merge(arguments[i]);
    }

    return extended;

};

हालांकि यह लिंक प्रश्न का उत्तर दे सकता है, लेकिन उत्तर के आवश्यक भागों को शामिल करना और संदर्भ के लिए लिंक प्रदान करना बेहतर है। लिंक-केवल उत्तर अमान्य हो सकते हैं यदि लिंक किए गए पृष्ठ बदल जाते हैं। - रिव्यू से
क्रिस कैमारट्टा

हाय @ChrisCamaratta न केवल आवश्यक हिस्सा यहां है, यह सब यहां है - फ़ंक्शन और इसका उपयोग कैसे करें। तो यह निश्चित रूप से केवल उत्तर की एक कड़ी नहीं है। यह वह फ़ंक्शन है जो मैं गहरी मर्ज ऑब्जेक्ट्स के लिए उपयोग कर रहा हूं। लिंक केवल तभी है जब आप चाहते हैं कि लेखक कैसे काम करे। मुझे लगता है कि यह लेखक को जावास्क्रिप्ट सिखाने वाले लेखक की तुलना में कामकाज को बेहतर ढंग से समझाने और समझाने के लिए एक असहमति होगी। टिप्पणी के लिए धन्यवाद।
जॉन शियरिंग

हुह। या तो मैंने इसे याद किया या जब मैंने इसकी समीक्षा की तो कोड समीक्षक इंटरफ़ेस में दिखाई नहीं दिया। मैं मानता हूं कि यह एक गुणवत्तापूर्ण उत्तर है। यह अन्य समीक्षकों को मेरा प्रारंभिक मूल्यांकन ओवरराइड करेगा, इसलिए मुझे लगता है कि आप ठीक हैं। प्रेरणा ध्वज के लिए क्षमा करें।
क्रिस केमराटा

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