npm i merge-anything
دمج الكائنات والأنواع الأخرى بشكل متكرر. دعم TypeScript بالكامل! تكامل بسيط وصغير.
لقد قمت بإنشاء هذه الحزمة لأنني جربت الكثير من الحزم المشابهة التي تقوم بدمج/دمج عميق/تعيين كائنات متكررة وما إلى ذلك. لكن جميعها كانت بها مراوغات، وكلها تكسر أشياء ليس من المفترض أن تكسرها ...؟
كنت أبحث عن:
Object.assign()
ولكنها عميقة هذا الأخير أمر بالغ الأهمية! في JavaScript، كل شيء تقريبًا عبارة عن كائن ، بالتأكيد، لكنني لا أريد أن تحاول وظيفة الدمج الدمج على سبيل المثال. حالتان new Date()
! تستخدم العديد من المكتبات فئات مخصصة تقوم بإنشاء كائنات ذات نماذج أولية خاصة، وتنكسر جميع هذه الكائنات عند محاولة دمجها. لذلك علينا أن نكون حذرين!
سيؤدي دمج أي شيء إلى دمج الكائنات والخصائص المتداخلة، ولكن فقط طالما أنها "كائنات عادية". بمجرد أن لا تصبح الدعامة الفرعية "كائنًا عاديًا" ولديها نموذج أولي خاص، فسوف تقوم بنسخ هذا المثيل على "كما هو". ♻️
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'
// }
في المثال أعلاه، إذا كنت تستخدم TypeScript، وقمت بالتمرير فوق evolution
، فيمكنك بالفعل رؤية نوع الكائن الجديد الخاص بك في ذلك الوقت وهناك. يعد هذا أمرًا قويًا جدًا، لأنه يمكنك دمج الأشياء، ودون الحاجة إلى any
، ستعرف TypeScript بالضبط كيف تبدو الكائنات المدمجة حديثًا!
يمكن استخدام نوع الإرجاع للدالة merge()
كأداة مساعدة لـ 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 ] >
سوف تمر هذه الحزمة بشكل متكرر عبر الكائنات العادية وتدمج القيم في كائن جديد.
يرجى ملاحظة أن هذه الحزمة تتعرف على كائنات JavaScript خاصة مثل مثيلات الفئة. في مثل هذه الحالات، لن يتم دمجها بشكل متكرر مثل الكائنات، ولكن سيتم تعيين الفئة إلى الكائن الجديد "كما هو"!
// 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: {}}
دمج أي شيء بشكل صحيح يحافظ على الكائنات الخاصة سليمة مثل التواريخ والتعبير العادي والوظائف ومثيلات الفصل وما إلى ذلك.
ومع ذلك، من المهم جدًا أن تفهم كيفية التعامل مع مراجع كائنات JavaScript. يرجى التأكد من قراءة #a note حول مراجع كائنات JavaScript أدناه.
السلوك الافتراضي هو أن تتم الكتابة فوق المصفوفات. يمكنك استيراد mergeAndConcat
إذا كنت بحاجة إلى سلسلة المصفوفات. لكن لا تقلق إذا لم تكن بحاجة إلى هذا، فهذه المكتبة قابلة للاهتزاز ولن تستورد كودًا لا تستخدمه!
import { mergeAndConcat } from 'merge-anything'
mergeAndConcat (
{ nested : { prop : { array : [ 'a' ] } } } ,
{ nested : { prop : { array : [ 'b' ] } } }
)
// returns { nested: { prop: { array: ['a', 'b'] } } },
قد تكون هناك أوقات تحتاج فيها إلى تعديل المنطق عند دمج شيئين. يمكنك توفير وظيفتك المخصصة التي يتم تشغيلها في كل مرة تتم فيها الكتابة فوق قيمة.
في هذه الحالة نستخدم mergeAndCompare
. فيما يلي مثال لدالة مقارنة تقوم بتسلسل السلاسل:
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' }
ملاحظة لمستخدمي TypeScript. قد لا يكون النوع الذي يتم إرجاعه بواسطة هذه الدالة صحيحًا. في هذه الحالة عليك أن ترسل النتيجة إلى الواجهة المتوفرة لديك
كن حذرًا بشأن مرجع كائن JavaScript. أي خاصية متداخلة ستكون تفاعلية ومرتبطة بين الكائنات الأصلية والكائنات المدمجة! أدناه سنوضح كيفية منع ذلك.
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. ?'
القاعدة الأساسية التي يجب تذكرها هي:
أي خاصية متداخلة على أكثر من مستوى واحد بدون خاصية أصل متداخلة ستكون تفاعلية ومرتبطة في كل من نتيجة الدمج والمصدر
ومع ذلك، هناك حل سهل حقا . يمكننا فقط نسخ نتيجة الدمج للتخلص من أي تفاعل. لهذا يمكننا استخدام مكتبة نسخ أي شيء. تتأكد هذه المكتبة أيضًا من عدم تعطل مثيلات الفئة الخاصة ، لذا يمكنك استخدامها دون خوف من تعطل الأشياء!
انظر أدناه كيفية دمج "نسخ أي شيء":
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
يمكنك بعد ذلك التجول في المكان الذي تريد وضع وظيفة copy()
.
نسخ أي شيء هو أيضًا مدعوم بالكامل من TypeScript!
إنه حرفيًا مجرد المرور عبر كائن بشكل متكرر وتعيين القيم لكائن جديد كما هو موضح أدناه. ومع ذلك، فقد تم تغليفه للسماح بمعلمات إضافية وما إلى ذلك. الكود أدناه هو التكامل الأساسي، الذي سيجعلك تفهم الأساسيات وكيفية عمله.
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 )
}
* بالطبع هناك اختلافات بسيطة مع الكود المصدري الفعلي للتعامل مع الحالات النادرة والميزات الإضافية. كود المصدر الفعلي هنا.