毫无变化地从物体上去除价值


96

在不更改原始对象的情况下,从特定键处的对象中删除值的一种好又短的方法是什么?

我想做类似的事情:

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

我知道有很多用于此类任务的不变性库,但是我想在没有库的情况下摆脱困境。但是要做到这一点,将需要一种可以在整个代码中使用的简便方法,而又不将方法抽象为实用函数。

例如,要添加一个值,请执行以下操作:

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

这很短,容易记住,不需要实用程序功能。

有这样的东西来删除值吗?ES6非常受欢迎。

非常感谢你!


“删除值而不更改对象”没有任何意义。您不能从某处移走并保持其完好无损。您实际上正在做的是部分复制。
JJJ

Answers:


215

更新:

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

const doSomething = (obj, prop) => {
  let {[prop]: omit, ...res} = obj
  return res
}

但是,如果要删除的属性名称是静态的,则可以使用简单的单行代码将其删除:

let {lastname, ...o2} = o

最简单的方法是简单地进行操作,或者您可以在对对象进行突变之前对其进行克隆:

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

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

let o2 = _.omit(o, 'lastname')

它可以作为lodash软件包的一部分,也可以作为独立的lodash.omit软件包使用。


3
那真的是最简单的解决方案吗?例如,对于数组,您可以a2 = a1.filter(el => el.id !== id)通过特定ID省略数组中的元素。物体没有这样的东西吗?
amann

1
我同意@amann。要么有一个更好的解决方案,或ES6确实错过了一些有关使JS可在一个功能更强大的方式。例如。露比(Ruby)keep_if
奥古斯丁·里丁格

1
@AugustinRiedinger实际上,有一种方法。查看我的更新。
Leonid Beschastny

我本来打算按照同样的建议对这个问题:stackoverflow.com/questions/35152522/...
奥古斯丁Riedinger

1
更新的解构解决方案很棒。虽然我的想法有些震惊。为什么不行const {[prop], ...rest} = obj?为什么必须将提取的属性分配给新变量(omit在这种情况下)?
branweb

28

使用ES7对象销毁:

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

1
如果a存储在变量中该const x = 'a'怎么办?
FFF

没有改变。仅指数组的第一个元素。可能是这样的:const { a,b, ...noA } = myObject; console.log(noA); // => {c: 3 }

1
这是最优雅,最简单的解决方案

1
如果键是数字,有没有办法做到这一点?
Marc Sl​​oth Eastman

22

一线解决方案

const removeKey = (key, {[key]: _, ...rest}) => rest;

4

如以上评论中所建议,如果您想扩展此功能以从object我要使用的项目中删除多个项目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;
    }


1
为什么不在reduce中应用过滤条件,这样可以节省一个循环?
伊沃·萨贝夫

感谢您的提示@IvoSabev我的代码中有一些函数,在此我进行过滤然后减少操作,但会考虑您的建议以保存循环。
ak85

3

添加一些香料以提高性能。波纹管检查

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

对于V8隐藏类模式,使用delete运算符会对性能产生负面影响。通常,建议不要使用它。

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

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

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

o.prop =未定义

您也可以使用破坏方式

const {remov1, remov2, ...new} = old;
old = new;

还有一个更实际的例子:

this._volumes[this._minCandle] = undefined;
{ 
     const {[this._minCandle]: remove, ...rest} = this._volumes;
     this._volumes = rest; 
}

如您所见,可以将[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;
    }, {});
}

对于打字稿

function deleteProps(obj: Object, props: string[]) {
    if (!Array.isArray(props)) props = [props];
    return Object.keys(obj).reduce((newObj, prop) => {
        if (!props.includes(prop)) {
            newObj[prop] = obj[prop];
        }
        return newObj;
    }, {});
}

用法:

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

a = deleteProps(a, 'propB'); 

// or 

a = deleteProps(a, ['propB', 'propO']);

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

关联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.

未定义虽然不适用于大列表。否则随着时间的发展,会有很多道具加入。由于内存使用量将持续增长,并且永远不会被清除。因此,这取决于用法。仅仅创建一个新对象似乎是个好方法。

然后 Premature optimization is the root of all evil将开始。因此,您需要意识到这一折衷。以及需要什么,不需要什么。

关于lodash的_.omit()注意

它已从版本5中删除。您无法在存储库中找到它。这里是一个谈论它的问题。

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

v8

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


0

与lodash cloneDeep和删除

(注意:lodash克隆可代替浅对象使用)

const obj = {a: 1, b: 2, c: 3}
const unwantedKey = 'a'

const _ = require('lodash')
const objCopy = _.cloneDeep(obj)
delete objCopy[unwantedKey]
// objCopy = {b: 2, c: 3}

0

对于我的代码,我想要map()的返回值的简短版本,但是多行/多操作运算的解决方案却“丑陋”。关键功能是旧的void(0),决心解决undefined

let o2 = {...o, age: 31, lastname: void(0)};

该属性保留在对象中:

console.log(o2) // {firstname: "Jane", lastname: undefined, age: 31}

但是传输框架会帮我杀死它(bc stringify):

console.log(JSON.stringify(o2)) // {"firstname":"Jane","age":31}

0

如果您尝试解构,我的问题是来自ESLint规则标准的可接受的答案:

    const { notNeeded, alsoNotNeeded, ...rest } = { ...ogObject };

2个新的变量,notNeeded并且alsoNotNeeded可能会抛出根据您的设置,因为他们现在未使用的警告或错误。那么,为什么要创建未使用的新变量呢?

我认为您需要delete真正使用该功能。


3
现在,该no-unused-vars规则实际上有一个选项ignoreRestSiblings可以覆盖此用例:eslint.org/docs/rules/no-unused-vars#ignorerestsiblings
amann
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.