对象扩展或深度合并,没有任何覆盖(改为创建值数组)

时间:2018-01-19 22:29:16

标签: javascript object

更新:最后我得到了一个有效的解决方案https://jsfiddle.net/olehmelnyk/oz7ez881/20/,但它看起来过于复杂,需要重构;

处理元标记解析器。 我正在寻找一个简单的JavaScript解决方案来扩展/深度合并两个对象,没有任何覆盖。我希望将目标中的原始值和源中的新值存储为数组,而不是覆盖。

输入 - 一次只有一个对象,在这种情况下,每个对象一次只包含一个值,但是这个值可以处于任何深层次,如下所示:

let obj1 = {
  og: {
    image: {
      src: 'http://'
    }
  }
};

let obj2 = {
  og: {
    image: {
      width: 640
    }
  }
};

let obj3 = {
  og: {
    image: {
      height: 320
    }
  }
};

let obj4 = {
  og: {
    image: {
      src: 'http://'
    }
  }
};

let obj5 = {
  og: {
    image: {
      src: 'http://'
    }
  }
};

let obj6 = {
  og: {
    image: {
      width: 640
    }
  }
};

期望输出应该是这样的:

{
  og: {
    image: [
      {src: '...', width: 640, height: 320},
      {src: '...'},
      {src: '...', width: 640}
    ]
  }
}

可能的伪代码(我不确定正确的算法):
- 1.检查我们的目标中是否存在与来源类似的内容 - 1.1如果不是 - 我们只是将源添加到目标
- 1.2否则 - 我们检查是否添加了唯一的源值
- 1.2.1如果是,我们可以将源与目标合并 - 1.2.2否则我们创建一个数组,其中包含目标的原始值和源

的新值

1 个答案:

答案 0 :(得分:0)

冬青牛,我做到了! 现在它按预期工作,但代码看起来过于复杂,因此需要重构。

function objectExtend(target, source) {
    let flatSource = flattenObject(source); // {og.image.src: 'http://'}

    let flatSourceKey = Object.keys(flatSource)[0]; // og.image.src
    let sourceKeys = flatSourceKey.split('.'); // ['og', 'image', 'src']

    let sourceKey = sourceKeys.slice(-1); // 'src'
    let sourceValue = flatSource[flatSourceKey]; // 'http://'
    let sourceKeyValue = {[sourceKey]: sourceValue}; // {src: 'http://'}
    let sourcePath = sourceKeys.slice(0, -1).join('.'); // og.image

    let flatTarget = flattenObject(target); // target with array of all key-values flattened
    let flatTargetKeys = Object.keys(flatTarget); // array of all flattened target keys
    let targetValue = flatTarget[flatSourceKey]; // target value (if any)

    let targetValueIsArray = Array.isArray(flatTarget[sourcePath]);
    let targetLastValue = targetValue;
    let targetArray;
    if(targetValueIsArray){
        targetArray = flatTarget[sourcePath];
        targetLastValue = targetArray[targetArray.length - 1];
    }

    let targetKey;
    try{
        targetKey = eval(`target.${sourcePath}`);
    }catch(e){/*we don't care*/}

    if(!flatTargetKeys.includes(flatSourceKey) && !targetValueIsArray){
        // because source will contain og.image.width, but after creating an array width will be in array, so og.image.width will look like unique key
        mergeDeep(target, source);
    }else if(flatTargetKeys.includes(flatSourceKey) && !targetValueIsArray){
        // create array with 2 values - current object[key] and source object[key] - we put object with src/width/heigth object
        mergeDeep(target, unflattenObject(sourcePath, [targetKey, sourceKeyValue]));
    }else if(targetValueIsArray){
        targetLastValue.hasOwnProperty(sourceKey)
            ? targetArray.push(sourceKeyValue) // create new array value array.push
            : targetArray.push(Object.assign(targetArray.pop(), sourceKeyValue)); // merge with last array value Object.assign()

        targetKey = targetArray;
    }
}

https://jsfiddle.net/olehmelnyk/oz7ez881/20/