_.assign仅当目标对象中存在属性时

时间:2015-01-23 07:16:32

标签: javascript lodash

我需要做一些像_.assign这样的事情,但前提是目标对象已经分配了属性。可以想象它就像源对象可能有一些属性要贡献,但也有一些我不想混合的属性。

我还没有使用_.assign的回调机制,但尝试了以下内容。它“工作”,但它仍然将属性分配给dest对象(未定义)。我根本不希望它分配。

_.assign(options, defaults, initial, function (destVal, sourceVal) {
  return typeof destVal == 'undefined' ? undefined : sourceVal;
});

我编写了以下函数来执行此操作,但想知道lodash是否已经有更好的烘焙内容。

function softMerge (dest, source) {
    return Object.keys(dest).reduce(function (dest, key) {
      var sourceVal = source[key];

      if (!_.isUndefined(sourceVal)) {
        dest[key] = sourceVal;
      }

      return dest;
    }, dest);
}

4 个答案:

答案 0 :(得分:16)

你可以只从第一个对象中取出钥匙

var firstKeys = _.keys(options);

然后从第二个对象中获取一个子集对象,仅获取第一个对象上存在的那些键:

var newDefaults = _.pick(defaults, firstKeys);

然后使用该新对象作为_.assign的参数:

_.assign(options, newDefaults);

或者在一行中:

_.assign(options, _.pick(defaults, _.keys(options)));

我在这里测试时似乎工作了:http://jsbin.com/yiyerosabi/1/edit?js,console

答案 1 :(得分:0)

这是一个不可变的深层版本,我称之为“合并,保留形状”,在使用lodash的TypeScript中:

function _mergeKeepShapeArray(dest: Array<any>, source: Array<any>) {
    if (source.length != dest.length) {
        return dest;
    }
    let ret = [];
    dest.forEach((v, i) => {
        ret[i] = _mergeKeepShape(v, source[i]);
    });
    return ret;
}

function _mergeKeepShapeObject(dest: Object, source: Object) {
    let ret = {};
    Object.keys(dest).forEach((key) => {
        let sourceValue = source[key];
        if (typeof sourceValue !== "undefined") {
            ret[key] = _mergeKeepShape(dest[key], sourceValue);
        } else {
            ret[key] = dest[key];
        }
    });
    return ret;
}

function _mergeKeepShape(dest, source) {
    // else if order matters here, because _.isObject is true for arrays also
    if (_.isArray(dest)) {
        if (!_.isArray(source)) {
            return dest;
        }
        return _mergeKeepShapeArray(dest, source);
    } else if (_.isObject(source)) {
        if (!_.isObject(source)) {
            return dest;
        }
        return _mergeKeepShapeObject(dest, source);
    } else {
        return source;
    }
}

/**
 * Immutable merge that retains the shape of the `existingValue`
 */
export const mergeKeepShape = <T>(existingValue: T, extendingValue): T => {
    return _mergeKeepShape(existingValue, extendingValue);
}

一个简单的测试,看看我如何看待这样的合并应该有效:

let newObject = mergeKeepShape(
    {
        a : 5,
        // b is not here
        c : 33,
        d : {
            e : 5,
            // f is not here
            g : [1,1,1],
            h : [2,2,2],
            i : [4,4,4],
        }
    },
    {
        a : 123,
        b : 444,
        // c is not here
        d : {
            e : 321,
            f : 432,
            // g is not here
            h : [3,3,3],
            i : [1,2],
        }
    }
);
expect(newObject).toEqual({
    a : 123,
    // b is not here
    c : 33,
    d : {
        e : 321,
        // f is not here,
        g : [1,1,1],
        h : [3,3,3],
        i : [4,4,4]
    }
});

我在测试中使用了无缝不可变的自己,但是没有必要把它放在这个答案中。

我将此放在公共领域。

答案 2 :(得分:0)

实现此目标的另一种方法是将_.mapObject_.has

合并
_.mapObject(object1, function(v, k) {
    return _.has(object2, k) ? object2[k] : v;
});

说明:

  1. 使用object1
  2. 遍历_.mapObject的所有键/值对
  3. 使用_.has,检查k中是否还存在属性名称object2
  4. 如果是,请将分配给密钥object2的{​​{1}}的值复制回k,否则,只返回object1(object1)的现有值。
  5. Plunkr

答案 3 :(得分:0)

在@svarog的回答之后,我想到了这个(lodash版本4.17.15):

const mergeExistingProps = (target, source) => _.mapValues(target, (value, prop) => _.get(source, prop, value));