【问题标题】:Can this solution to merge two objects dynamically be simpler?这种动态合并两个对象的解决方案可以更简单吗?
【发布时间】:2021-03-26 11:55:48
【问题描述】:

如果我有这个root 对象:

const root = {
    list: {
        first: false,
        second: false,
        third: false,
        forth: false
    }
};

并且想要创建一个具有相同结构但更改单个变量的新对象。我可以通过以下方式做到这一点:

const result = {
    ...root,
    list: {
        ...root.list,
        first: true
    }
}

所以结果会是:

{
  "list": {
    "first": true,
    "second": false,
    "third": false,
    "forth": false
  }
}

一切正常,但我需要以动态方式做同样的事情。假设我传递了root 对象,需要更新的属性的完整路径"list.first" 作为字符串 和一个值。我找到了解决方案:

const merge = require('lodash').merge;

const root = {
    list: {
        first: false,
        second: false,
        third: false,
        forth: false
    }
};

function createObjectByPathAndValue(path, value) {
    const keys = (path || '').split('.') || [];

    function createObjectByPathAndValue(root, keys, value) {
        if (keys.length === 1) {
            root[keys[0]] = value;
        } else {
            const key = keys.splice(0, 1);
            root[key] = createObjectByPathAndValue({}, keys, value);
        }
        return root;
    }

    const result = createObjectByPathAndValue({}, keys, value);
    return result;
}

const changes = createObjectByPathAndValue('list.first', true);

const result = merge({}, root, changes);

console.log(JSON.stringify(result, null,2 ));

但我们承认它很麻烦,而且代码太多。我认为我没有使用现代 javascript 的所有可能性,它可以做得更简单。我说的对吗?

附:必须创建一个新对象,即不允许改变现有对象(我需要它来更新 React 的状态)

【问题讨论】:

  • 您的实际问题是什么?合并两个对象还是创建一个对象?合并部分是两行。还是您的实际问题是如何深度克隆对象并更改一个值?
  • 其实我需要创建一个新对象,修改后的属性由属性path.to.property的路径和值指定。
  • 标题具有误导性。这不是合并两个对象。这是将(深度)属性设置为一个值。罗达什set.

标签: javascript ecmascript-6 javascript-objects


【解决方案1】:

您可以深度克隆您的对象并更改值:

const {cloneDeep, toPath, set} = require('lodash');

const root = {
    list: {
        first: false,
        second: false,
        third: false,
        forth: false
    }
};

const result = cloneDeep(root);
set(result, toPath('list.first'), true);

console.log(JSON.stringify(result, null, 2));

这是一个例子(jsfiddle 不支持require):

const root = {
    list: {
        first: false,
        second: false,
        third: false,
        forth: false
    }
};

const result = _.cloneDeep(root);
_.set(result, _.toPath('list.first'), true);

console.log(JSON.stringify(result, null, 2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

【讨论】:

  • 好的,现在说说你是怎么从"result.list.first"result.list.first的,注意第一个是字符串,第二个是对象。
  • 我更新了我的答案,因为它可能不清楚,我已经通过字符串给出了"path.to.property",我需要将其转换为实际对象。
  • @Anatoly 我修好了。
  • @Anatoly Lodash 有一个 set 函数
  • 顺便说一句,lodash 的 fp 版本是 set,它创建了一个新对象:stackoverflow.com/a/36972679/947111
【解决方案2】:

我最后使用了lodash/fn/set 包中的set 函数,如this answer 中所述

const setFp = require('lodash/fp/set');

const root = {
    list: {
        first: false,
        second: false,
        third: false,
        forth: false
    }
};

const newRoot = setFp(`list.first`, true, root); //a new object will be created

阅读更多关于lodash/fp 包的信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-22
    • 1970-01-01
    相关资源
    最近更新 更多