从对象中删除值而不进行变异

ama*_*ann 77 javascript immutability

在不改变原始对象的情况下从特定键的对象中删除值的好方法和简短方法是什么?

我想做点什么:

let o = {firstname: 'Jane', lastname: 'Doe'};
let o2 = doSomething(o, 'lastname');
console.log(o.lastname); // 'Doe'
console.log(o2.lastname); // undefined
Run Code Online (Sandbox Code Playgroud)

我知道有很多不可变的库用于这样的任务,但我想在没有库的情况下离开.但要做到这一点,一个要求是有一个简单而简短的方法可以在整个代码中使用,而不会将方法作为效用函数抽象出来.

例如,为了添加值,我执行以下操作:

let o2 = {...o1, age: 31};

这很简单,易于记忆,不需要实用功能.

是否有类似的东西去除值?ES6非常受欢迎.

非常感谢你!

Leo*_*tny 174

更新:

您可以通过棘手的Destructuring赋值从对象中删除属性:

const doSomething = (obj, prop) => {
  let {[prop]: omit, ...res} = obj
  return res
}
Run Code Online (Sandbox Code Playgroud)

但是,如果要删除的属性名称是静态的,那么您可以使用简单的单行删除它:

let {lastname, ...o2} = o
Run Code Online (Sandbox Code Playgroud)

最简单的方法就是或者你可以在变异之前克隆你的对象:

const doSomething = (obj, prop) => {
  let res = Object.assign({}, obj)
  delete res[prop]
  return res
}
Run Code Online (Sandbox Code Playgroud)

另外,您可以使用omit从功能lodash实用程序库:

let o2 = _.omit(o, 'lastname')
Run Code Online (Sandbox Code Playgroud)

它作为lodash包的一部分提供,或作为独立的lodash.omit包提供.

  • 这真的是最简单的解决方案吗?例如,对于数组,你可以做`a2 = a1.filter(el => el.id!== id)`来通过特定的id省略数组中的元素.对象没有这样的东西吗? (2认同)
  • 更新的解构解决方案很棒。虽然我的心有点被它震撼了。为什么`const {[prop], ...rest} = obj` 不起作用?为什么必须将提取的属性分配给新变量(在本例中为 `omit`)? (2认同)

sen*_*bon 17

使用ES7对象解构:

const myObject = {
  a: 1,
  b: 2,
  c: 3
};
const { a, ...noA } = myObject;
console.log(noA); // => { b: 2, c: 3 }
Run Code Online (Sandbox Code Playgroud)

  • 这是最优雅、最简单的解决方案 (2认同)
  • 如果键是数字,有没有办法做到这一点? (2认同)
  • 如果我错了,请纠正我,但如果要删除的键是动态的(来自变量),例如 `const {[keyNameOfA], ...noA} = myObject`,则这是不可能的 (2认同)

pun*_*sta 14

一线解决方案

const removeKey = (key, {[key]: _, ...rest}) => rest;
Run Code Online (Sandbox Code Playgroud)


ak8*_*k85 5

正如上面评论中所建议的,如果您想扩展它以从您的objectI like to use 中删除多个项目filter。和reduce

例如

    const o = {
      "firstname": "Jane",
      "lastname": "Doe",
      "middlename": "Kate",
      "age": 23,
      "_id": "599ad9f8ebe5183011f70835",
      "index": 0,
      "guid": "1dbb6a4e-f82d-4e32-bb4c-15ed783c70ca",
      "isActive": true,
      "balance": "$1,510.89",
      "picture": "http://placehold.it/32x32",
      "eyeColor": "green",
      "registered": "2014-08-17T09:21:18 -10:00",
      "tags": [
        "consequat",
        "ut",
        "qui",
        "nulla",
        "do",
        "sunt",
        "anim"
      ]
    };

    const removeItems = ['balance', 'picture', 'tags']
    console.log(formatObj(o, removeItems))

    function formatObj(obj, removeItems) {
      return {
        ...Object.keys(obj)
          .filter(item => !isInArray(item, removeItems))
          .reduce((newObj, item) => {
            return {
              ...newObj, [item]: obj[item]
            }
          }, {})
      }
    }

    function isInArray(value, array) {
      return array.indexOf(value) > -1;
    }
Run Code Online (Sandbox Code Playgroud)


Moh*_*lal 5

添加一些香料,带来性能。检查这个线程波纹管

https://github.com/googleapis/google-api-nodejs-client/issues/375

删除操作符的使用对 V8 隐藏类模式有性能负面影响。一般来说,建议不要使用它。

或者,要删除对象自己的可枚举属性,我们可以创建一个没有这些属性的新对象副本(例如使用 lodash):

_.omit(o, 'prop', 'prop2')

或者甚至将属性值定义为 null 或 undefined(在序列化为 JSON 时会隐式忽略):

o.prop = 未定义

你也可以使用破坏性的方式

const {remov1, remov2, ...new} = old;
old = new;
Run Code Online (Sandbox Code Playgroud)

还有一个更实用的例子:

this._volumes[this._minCandle] = undefined;
{ 
     const {[this._minCandle]: remove, ...rest} = this._volumes;
     this._volumes = rest; 
}
Run Code Online (Sandbox Code Playgroud)

如您所见,您可以将[somePropsVarForDynamicName]: scopeVarName语法用于动态名称。您可以将所有内容放在括号中(新块),以便其余部分在它之后被垃圾收集。

这里有一个测试: 在此处输入图片说明

执行:

在此处输入图片说明

或者我们可以使用一些功能,例如

function deleteProps(obj, props) {
    if (!Array.isArray(props)) props = [props];
    return Object.keys(obj).reduce((newObj, prop) => {
        if (!props.includes(prop)) {
            newObj[prop] = obj[prop];
        }
        return newObj;
    }, {});
}
Run Code Online (Sandbox Code Playgroud)

打字稿

const {remov1, remov2, ...new} = old;
old = new;
Run Code Online (Sandbox Code Playgroud)

用法:

let a = {propH: 'hi', propB: 'bye', propO: 'ok'};

a = deleteProps(a, 'propB'); 

// or 

a = deleteProps(a, ['propB', 'propO']);
Run Code Online (Sandbox Code Playgroud)

这样就创建了一个新对象。并且保持了对象的快速属性。这可能很重要或很重要。如果映射和对象会被多次访问。

关联undefined也是一个很好的方式。当你买得起的时候。对于键,您也可以检查值。例如,要获取所有活动键,您可以执行以下操作:

const allActiveKeys = Object.keys(myObj).filter(k => myObj[k] !== undefined);
//or
const allActiveKeys = Object.keys(myObj).filter(k => myObj[k]); // if any false evaluated value is to be stripped.
Run Code Online (Sandbox Code Playgroud)

未定义虽然不适合大列表。或者随着时间的推移,有许多道具进入。由于内存使用量将不断增长并且永远不会被清理。所以这取决于用途。只是创建一个新对象似乎是个好方法。

然后Premature optimization is the root of all evil就会开始。所以你需要知道权衡。什么是需要的,什么不是。

关于 _.omit() 来自 lodash 的注意事项

它已从版本 5 中删除。您在 repo 中找不到它。这里有一个谈论它的问题。

https://github.com/lodash/lodash/issues/2930

v8

你可以检查这是一个很好的阅读https://v8.dev/blog/fast-properties