首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Object.assign或类似的模式扩展数组而不是覆盖数组

使用Object.assign或类似的模式扩展数组而不是覆盖数组
EN

Stack Overflow用户
提问于 2017-06-24 19:06:48
回答 3查看 336关注 0票数 1

我正在寻找如何确定何时覆盖或扩展数组/集合的通用解决方案。

假设我们有这样的目标:

代码语言:javascript
复制
let obj1 = {

  rolo: 'bar',
  yolo: true,
  cholo: [1,2,3,4]

};

如果我打电话

代码语言:javascript
复制
let obj2 = Object.assign({}, obj1, {
  cholo: [5]
});

我去拿这个:

代码语言:javascript
复制
   obj2 => { rolo: 'bar', yolo: true, cholo: [ 5 ] }

但我要找的是:

代码语言:javascript
复制
  obj2 => { rolo: 'bar', yolo: true, cholo: [ 1, 2, 3, 4, 5 ] }

我能用来扩展数组而不是覆盖的最佳模式是什么?我特别感兴趣的是在库代码的上下文中这样做。其中,用户可以选择是扩展数组还是覆盖数组。

我知道没有简单的模式可以用来做这件事.

例如,我们可以这样做:

代码语言:javascript
复制
  override: {

      metadata:{
        cholo: {
           extend: true
        }
      },

      value: {
        cholo: [5]
      }
   }

添加一些元数据以配合新对象。然后,我将有一些自定义函数,它不仅调用Object.assign,而且检查元数据属性,并手动复制属性。

就像这样:

代码语言:javascript
复制
let copier = function(defaultObj, {value, metadata}){

   let allKeysDupes = Object.keys(value).concat(Object.keys(defaultObj));
   let allKeys = allKeysDupes.filter(i => allKeysDupes.indexOf(v === i));
   let ret = {}; 

   // manually go through all keys
   // check metadata to determine whether to overwrite or extend arrays, etc.

   allKeys.forEach(function(k){

     if(metadata[k]{
       // check the metadata and follow it's rules


     }
     else if(k in val){
       ret[k] = val[k];
     }
     else {
       ret[k] = defaultObj[k];
     }

   });

   return ret;
}
EN

回答 3

Stack Overflow用户

发布于 2017-06-25 23:48:12

使用ES6代理的通用解决方案

下面是利用ES6代理 (在ECMAScript 2015语言规范 (ES6)中引入)来对传递的任何普通对象执行可定制的深度合并和连接的通用解决方案。该函数允许为所遇到的每种类型的输入值轻松自定义合并过程,并且应该能够处理任何类型的输入值,只要它传递的对象通常与Object.assign()一起工作。

解释

准确获取类型

对于处理目的来说,容易而准确地识别输入值的类型是很重要的。要做到这一点,该函数利用Object.prototype.toString (对任意值调用时)返回一个沿着[object String][object Object]等方向的字符串。在那里,我们可以去掉不必要的位,最后得到一个字符串,比如StringObject等等。

代码语言:javascript
复制
const gettype = e => Object.prototype.toString.call(e).replace(/.*\b(\w+)./, '$1');

处理处理程序

我们需要简化不同数据类型的处理。你可以使用混乱的、硬编码的开关语句,但我更喜欢一些更流畅的语句。我喜欢使用函数对象。对于我们想要处理的每个类型,我们只需定义一个与上面定义的gettype函数的输出直接匹配的属性名称。然后,在需要时,我们可以根据相应的类型值查找该函数。

在本例中,我创建了一个默认的处理程序集,然后由函数的第一个参数中提供的处理程序覆盖。

代码语言:javascript
复制
Object.assign(h, {
    "Array": (a, b) => [...a, ...b],
    "Object": (a, b) => deepmerge(h, a, b),
    "Set": (a, b) => new Set([...a, ...b]),
    "Map": (a, b) => {
        const o = new Map([...a]);
        for(let [k,v] of [...b]) {
            const vt = gettype(v);
            o.set(k, a.has(k) && vt in h ? h[vt](a.get(k), v) : v);
        }
        return o;
    }
}, h);

使用代理拦截set操作

现在我们将进入代理部分。首先定义一个"out“对象。这个对象最终将由这个函数返回,并充当代理的目标。

代码语言:javascript
复制
const o = {};

接下来,使用set陷阱为"out“对象创建一个代理。当set操作发生时,set陷阱将原始值和新值传递给适当的set处理程序。如果没有适当的set处理程序,则用新值覆盖旧值。确定的值应用于"out“对象。

代码语言:javascript
复制
const p = new Proxy(o, {
    set: (o, k, b) => {
        const a = o[k];
        const at = gettype(a);
        return (o[k] = (at in h && k in o ? h[at](a, b) : b), true);
    }
});

接下来,使用Object.assign将对象合并到代理中,这将触发上面为每个set操作定义的set陷阱。

代码语言:javascript
复制
Object.assign(p, ...args);

最后返回"out“对象。

代码语言:javascript
复制
return o;

全定义

代码语言:javascript
复制
const deepmerge = (h, ...args) => {
    const gettype = e => Object.prototype.toString.call(e).replace(/.*\b(\w+)./, '$1');

    const o = {};

    Object.assign(h, {
        "Array": (a, b) => [...a, ...b],
        "Object": (a, b) => deepmerge(h, a, b),
        "Set": (a, b) => new Set([...a, ...b]),
        "Map": (a, b) => {
            const o = new Map([...a]);
            for(let [k,v] of [...b]) {
                const vt = gettype(v);
                o.set(k, a.has(k) && vt in h ? h[vt](a.get(k), v) : v);
            }
            return o;
        }
    }, h);

    const p = new Proxy(o, {
        set: (o, k, b) => {
            const a = o[k];
            const at = gettype(a);
            return (o[k] = (at in h && k in o ? h[at](a, b) : b), true);
        }
    });

    Object.assign(p, ...args);

    return o;
};

用法

代码语言:javascript
复制
deepmerge(handlers, ...sources)
  • 操作者 处理程序对象。该处理程序对象可用于覆盖给定类型的默认处理程序,或为default.Example{ "String":(a,b) => a+ b,"Number":(a,b) => a+ b,}未处理的类型提供处理程序。
代码语言:javascript
复制
- The name of each property should be the name of the type being handled, with the first letter capitalized.  
- The value of each property should be a function which accepts two arguments and returns the result of merging those arguments.  

  • 资料来源 正在合并的对象。
    • 每个被合并的对象必须是一个普通对象(没有映射、集合或数组等)。
    • 每个对象可能包含任何类型的值。

示例

下面的片段显示了两个示例:

  • 第一个只使用默认的处理程序。
  • 第二种方法使用一些自定义处理程序来演示合并过程的自定义。注意两个结果对象之间的差异。

代码语言:javascript
复制
const objects = [
    {
        string: "hello", 
        number: 1, 
        boolean: true,
        array: [1,2,3], 
        object: { a: 1, b: 2 }, 
        map: new Map([[1,2],[2,3],[4,2],[undefined, undefined],[null, null]]), 
        set: new Set([1,2,3]),
        null: null,
        undefined: undefined
    },
    { 
        string: " world", 
        number: 2, 
        boolean: false, 
        array: [4,5,6], 
        object: { a: 2, b: 1 }, 
        map: new Map([[1,1],[2,0],[3,1],[undefined, null],[null, undefined]]), 
        set: new Set([4,5,6]),
        null: undefined,
        undefined: null
    }
];

console.log(deepmerge({}, ...objects));
console.log(deepmerge({
    "String": (a, b) => a + b,
    "Number": (a, b) => a + b,
}, ...objects));
代码语言:javascript
复制
<script src="https://cdn.rawgit.com/Tiny-Giant/43cc03adf3cdc84ff935655cbebbe585/raw/754070ca8858efeff5a2c3b8bad6475842565798/deepmerge.js"></script><link rel="stylesheet" href="https://cdn.rawgit.com/Tiny-Giant/f2a53f469863baadf1b4ad48a4b4ea39/raw/b0ede74f374199abe9324334d1c0ef088a850415/deepmerge.css" type="text/css">

票数 2
EN

Stack Overflow用户

发布于 2017-06-24 19:20:15

我采用了,并根据您的需求进行了修改,引入了一个新的字段concat来指定您是否愿意连接。

代码语言:javascript
复制
function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}

function mergeDeep(concat = false, 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 {
        if(concat && Array.isArray(source[key]) && target[key]){
            target[key] = source[key].concat(target[key]);
        } else {
          Object.assign(target, { [key]: source[key] });
        }
      }
    }
  }
  return mergeDeep(concat, target, ...sources);
}

let obj1 = {
  rolo: 'bar',
  yolo: true,
  cholo: [1,2,3,4]
};

let arrObj = {
  cholo: [5]
};

console.log(mergeDeep(true, arrObj,{cholo:[2,3,4]}, obj1));

您可以创建一个新对象,然后将新对象的数组与旧对象连接起来。

代码语言:javascript
复制
let obj1 = {
  rolo: 'bar',
  yolo: true,
  cholo: [1,2,3,4]
};

let arrObj = {
  cholo: [5]
};

let obj2 = Object.assign({}, obj1, arrObj);

obj2['cholo'] = obj1['cholo'].concat(obj2['cholo']);
console.log(obj2);

票数 1
EN

Stack Overflow用户

发布于 2017-06-24 19:22:03

代码语言:javascript
复制
let obj2 = Object.assign({}, obj1, {
  cholo: obj1.cholo.concat([5])
});

按照你的建议:

代码语言:javascript
复制
let fn = function({value, metadata}) : Object{

   let defaults = obj1;
   let ret = {}; 

   // manually go through value
   // check metadata to determine whether to overwrite or extend
   for (var key in value) {
     if (metadata[key] != null) {
       if (isArray(value[key]) && isArray(metadata[key]) {
         ret[key] = value[key].concat(metadata[key])
       } else if (typeof value[key] === 'object' && typeof metadata[key] === 'object') {
         ret[key] = Object.assign(value[key], metadata[key]);
       } else {
         ret[key] = value[key]
       }
     }
   }

   return ret;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44739990

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档