_.assign только если свойство существует в целевом объекте
Мне нужно сделать что-то вроде _.assign, но только если целевой объект уже имеет назначенное свойство. Подумайте об этом, так как исходные объекты могут иметь некоторые свойства для внесения вклада, но также некоторые свойства, которые я не хочу смешивать.
Я никогда не использовал механизм обратного вызова _.assign, но попробовал следующее. Он "работал", но он по-прежнему привязывал свойство к объекту dest (как undefined). Я не хочу, чтобы он вообще назначался.
_.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);
}
Ответы
Ответ 1
Вы можете взять только ключи от первого объекта
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
Ответ 2
Вот неизменяемая глубокая версия, я называю это "объединением, которое сохраняет форму", в TypeScript, который использует lodash:
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(dest)) {
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]
}
});
Я сам использовал в тесте "без изменений", но не видел необходимости добавлять его в этот ответ.
Настоящим я помещаю это в общественное достояние.
Ответ 3
Другим способом добиться этого является объединение _.mapObject
с _.has
_.mapObject(object1, function(v, k) {
return _.has(object2, k) ? object2[k] : v;
});
Пояснение:
- Проведите все пары ключей/значений
object1
с помощью _.mapObject
- Используя
_.has
, проверьте, существует ли также имя свойства k
в object2
.
- Если это так, скопируйте значение, назначенное клавише
object2
k
, обратно на object1
, иначе просто верните существующее значение объекта1 (v
).
Plunkr