Mentre stavo lavorando ad un recente progetto in JavaScript, mi sono trovato nella necessità di dover fare il merge di un oggetto di valori di default con un secondo oggetto di valori custom. La complicazione stava nel fatto che i due oggetti potevano contenere a loro volta altri oggetti nidificati, e che l'operazione non poteva limitarsi a fare una shallow copy di questi oggetti figlio. Dopo un po' di lavoro, mergeObjectsDeep()
è nata per poter essere condivisa.
La Soluzione Proposta
La funzione che segue è il risultato finale del mix di tre ingredienti principali: la funzione Array.prototype.reduce()
, la Ricorsione e la Spread Syntax.
1/**
2 * Nell'eventualità ti trovassi a dover rinominare
3 * questa funzione, non dimenticare di rinominare
4 * anche la chiamata ricorsiva!
5 */
6const mergeObjectsDeep = ( baseObj, incomingObj ) => (
7 Object.keys( incomingObj ).reduce( ( mergedObj, key ) => {
8 if ( isObject( baseObj[key] ) && isObject( incomingObj[key] ) ) {
9 return {
10 ...mergedObj,
11 [key]: mergeObjectsDeep( baseObj[key], incomingObj[key] )
12 };
13 }
14
15 return {
16 ...mergedObj,
17 [key]: incomingObj[key]
18 };
19 }, baseObj )
20);
Sicuramente avrai notato due chiamate alla funzione custom isObject()
, bene, come suggerisce il suo nome, é una funzione modo-task il cui body può essere dispiegato come segue:
1const isObject = ( variable ) => {
2 return (
3 typeof variable === 'object' &&
4 !Array.isArray( variable ) &&
5 variable !== null
6 );
7};
Una Breve Spiegazione ed un Dettaglio Obbligatorio
Il codice funziona così:
- Quando i due valori a confronto non sono entrambi oggetti, utilizziamo la spread syntax per sostituire il valore in
baseObj
con il corrispondete inincomingObj
, questo se la chiave in esame esiste in entrambi gli oggetti, altrimenti il valore in esame (quello facente parte diincomingObj
) viene inserito inbaseObj
come una nuova coppia chiave-valore. - Quando, invece, i due valori messi a confronto sono entrambi oggetti, la ricorsione entra in gioco in coppia con la spread syntax per effettuare il deep-merge dei due oggetti figlio.
Voglio spostare un attimo la tua attenzione sulla funzione Array.prototype.reduce()
, usata in questo script per comporre l'oggetto finale restituito da mergeObjectsDeep()
.
Hai notato qual è il valore iniziale passato come secondo paramentro alla reduce()
?
Passare baseObj
alla funzione reduce()
quale valore iniziale da cui iniziare a costruire l'oggetto finale, è l'accortezza necessaria in assenza della quale mergeObjectsDeep()
non restituirebbe il risultato atteso.
La Spread Syntax, una Novità per Te?
Allora, ti incoraggio a dare una lettura alla sezione "Spread in object literals" di questo articolo ben scritto sul sito MDN Web Docs della Mozilla.