npm i merge-anything
Fusionnez des objets et d'autres types de manière récursive. Entièrement pris en charge par TypeScript ! Une intégration simple et petite.
J'ai créé ce package parce que j'ai essayé beaucoup de packages similaires qui effectuent des fusions/fusions profondes/attributions d'objets récursifs, etc. Mais tous avaient leurs bizarreries, et tous cassent des choses qu'ils ne sont pas censés casser ... ?
Je cherchais :
Object.assign()
mais profonde Ce dernier est crucial ! En JavaScript, presque tout est un objet , bien sûr, mais je ne veux pas qu'une fonction de fusion essaie de fusionner, par exemple. deux new Date()
! De nombreuses bibliothèques utilisent des classes personnalisées qui créent des objets avec des prototypes spéciaux, et ces objets se cassent tous lorsqu'on essaie de les fusionner. Il faut donc faire attention !
merge-anything fusionnera les objets et les propriétés imbriquées, mais seulement tant qu'il s'agit d'"objets simples". Dès qu'un sous-accessoire n'est pas un "objet simple" et possède un prototype spécial, il copiera cette instance "telle quelle". ♻️
import { merge } from 'merge-anything'
const starter = { name : 'Squirtle' , types : { water : true } }
const newValues = { name : 'Wartortle' , types : { fighting : true } , level : 16 }
const evolution = merge ( starter , newValues , { is : 'cool' } )
// returns {
// name: 'Wartortle',
// types: { water: true, fighting: true },
// level: 16,
// is: 'cool'
// }
Dans l'exemple ci-dessus, si vous utilisez TypeScript et que vous survolez evolution
, vous pouvez réellement voir le type de votre nouvel objet à ce moment-là. C'est très puissant, car vous pouvez fusionner des éléments, et sans any
avoir besoin, TypeScript saura exactement à quoi ressemblent vos objets nouvellement fusionnés !
Le type de retour de la fonction merge()
est également utilisable comme utilitaire TypeScript :
import type { Merge } from 'merge-anything'
type A1 = { name : string }
type A2 = { types : { water : boolean } }
type A3 = { types : { fighting : boolean } }
type Result = Merge < A1 , [ A2 , A3 ] >
Ce package parcourra de manière récursive les objets simples et fusionnera les valeurs sur un nouvel objet.
Veuillez noter que ce package reconnaît les objets JavaScript spéciaux comme les instances de classe. Dans de tels cas, il ne les fusionnera pas de manière récursive comme des objets, mais attribuera la classe au nouvel objet "tel quel" !
// all passed objects do not get modified
const a = { a : 'a' }
const b = { b : 'b' }
const c = { c : 'c' }
const result = merge ( a , b , c )
// a === {a: 'a'}
// b === {b: 'b'}
// c === {c: 'c'}
// result === {a: 'a', b: 'b', c: 'c'}
// However, be careful with JavaScript object references with nested props. See below: A note on JavaScript object references
// arrays get overwritten
// (for "concat" logic, see Extensions below)
merge ( { array : [ 'a' ] } , { array : [ 'b' ] } ) // returns {array: ['b']}
// empty objects merge into objects
merge ( { obj : { prop : 'a' } } , { obj : { } } ) // returns {obj: {prop: 'a'}}
// but non-objects overwrite objects
merge ( { obj : { prop : 'a' } } , { obj : null } ) // returns {obj: null}
// and empty objects overwrite non-objects
merge ( { prop : 'a' } , { prop : { } } ) // returns {prop: {}}
merge-anything conserve correctement les objets spéciaux intacts comme les dates, les expressions régulières, les fonctions, les instances de classe, etc.
Cependant, il est très important que vous compreniez comment contourner les références d'objets JavaScript. Assurez-vous de lire #une note sur les références d'objets JavaScript ci-dessous.
Le comportement par défaut est que les tableaux sont écrasés. Vous pouvez importer mergeAndConcat
si vous devez concaténer des tableaux. Mais ne vous inquiétez pas si vous n'en avez pas besoin, cette bibliothèque est modifiable en arborescence et n'importera pas le code que vous n'utilisez pas !
import { mergeAndConcat } from 'merge-anything'
mergeAndConcat (
{ nested : { prop : { array : [ 'a' ] } } } ,
{ nested : { prop : { array : [ 'b' ] } } }
)
// returns { nested: { prop: { array: ['a', 'b'] } } },
Il se peut que vous deviez parfois modifier la logique lorsque deux éléments sont fusionnés. Vous pouvez fournir votre propre fonction personnalisée qui se déclenche chaque fois qu'une valeur est écrasée.
Pour ce cas, nous utilisons mergeAndCompare
. Voici un exemple avec une fonction de comparaison qui concatène des chaînes :
import { mergeAndCompare } from 'merge-anything'
function concatStrings ( originVal , newVal , key ) {
if ( typeof originVal === 'string' && typeof newVal === 'string' ) {
// concat logic
return ` ${ originVal } ${ newVal } `
}
// always return newVal as fallback!!
return newVal
}
mergeAndCompare ( concatStrings , { name : 'John' } , { name : 'Simth' } )
// returns { name: 'JohnSmith' }
Remarque pour les utilisateurs de TypeScript. Le type renvoyé par cette fonction peut ne pas être correct. Dans ce cas, vous devez diffuser le résultat sur votre propre interface fournie.
Faites attention à la référence d'objet JavaScript. Toute propriété imbriquée sera réactive et liée entre l'objet d'origine et les objets fusionnés ! Ci-dessous, nous montrerons comment éviter cela.
const original = { airport : { status : 'dep. ?' } }
const extraInfo = { airport : { location : 'Brussels' } }
const merged = merge ( original , extraInfo )
// we change the status from departuring ? to landing ?
merged . airport . status = 'lan. ?'
// the `merged` value will be modified
// merged.airport.status === 'lan. ?'
// However `original` value will also be modified!!
// original.airport.status === 'lan. ?'
La règle clé à retenir est la suivante :
Toute propriété imbriquée sur plus d'un niveau sans propriété parent qui se chevauche sera réactive et liée à la fois dans le résultat de la fusion et dans la source.
Cependant, il existe une solution très simple . Nous pouvons simplement copier le résultat de la fusion pour supprimer toute réactivité. Pour cela, nous pouvons utiliser la bibliothèque copy-anything. Cette bibliothèque garantit également que les instances de classes spéciales ne se cassent pas , vous pouvez donc l'utiliser sans craindre de casser des choses !
Découvrez ci-dessous comment nous intégrons « copier n'importe quoi » :
import { copy } from 'copy-anything'
const original = { airport : { status : 'dep. ?' } }
const extraInfo = { airport : { location : 'Brussels' } }
const merged = copy ( merge ( original , extraInfo ) )
// we change the status from departuring ? to landing ?
merged . airport . status = 'lan. ?' ( merged . airport . status === 'lan. ?' ) (
// true
// `original` won't be modified!
original . airport . status === 'dep. ?'
) // true
Vous pouvez ensuite jouer où vous souhaitez placer la fonction copy()
.
Copy Anything est également entièrement pris en charge par TypeScript !
Il s'agit littéralement de parcourir un objet de manière récursive et d'attribuer les valeurs à un nouvel objet comme ci-dessous. Cependant, il est enveloppé pour autoriser des paramètres supplémentaires, etc. Le code ci-dessous est l'intégration de base, qui vous fera comprendre les bases de son fonctionnement.
import { isPlainObject } from 'is-what'
function mergeRecursively ( origin , newComer ) {
if ( ! isPlainObject ( newComer ) ) return newComer
// define newObject to merge all values upon
const newObject = isPlainObject ( origin )
? Object . keys ( origin ) . reduce ( ( carry , key ) => {
const targetVal = origin [ key ]
if ( ! Object . keys ( newComer ) . includes ( key ) ) carry [ key ] = targetVal
return carry
} , { } )
: { }
return Object . keys ( newComer ) . reduce ( ( carry , key ) => {
const newVal = newComer [ key ]
const targetVal = origin [ key ]
// early return when targetVal === undefined
if ( targetVal === undefined ) {
carry [ key ] = newVal
return carry
}
// When newVal is an object do the merge recursively
if ( isPlainObject ( newVal ) ) {
carry [ key ] = mergeRecursively ( targetVal , newVal )
return carry
}
// all the rest
carry [ key ] = newVal
return carry
} , newObject )
}
* Bien sûr, il existe de petites différences avec le code source réel pour faire face à des cas rares et à des fonctionnalités supplémentaires. Le code source réel est ici.