【问题标题】:Remove value from object without mutation从对象中删除值而不进行更改
【发布时间】:2015-10-10 11:09:18
【问题描述】:

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

我想做这样的事情:

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 很受欢迎。

非常感谢!

【问题讨论】:

  • “删除一个值而不改变对象”没有任何意义。您不能从某物上移除并同时保持它完好无损。您实际上在做的是制作部分副本。

标签: javascript immutability


【解决方案1】:

更新:

您可以使用棘手的Destructuring assignment 从对象中删除属性:

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 function from lodash utility library:

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

它可以作为lodash 包的一部分提供,也可以作为独立的lodash.omit 包提供。

【讨论】:

  • 这真的是最简单的解决方案吗?例如。对于数组,您可以执行 a2 = a1.filter(el => el.id !== id) 以通过特定 id 省略数组中的元素。对象就没有这样的东西吗?
  • 我同意@amann。要么有 更好的解决方案,要么 ES6 真的错过了让 JS 以更实用的方式使用的一些东西。例如。 Ruby 有keep_if
  • @AugustinRiedinger 实际上,有办法。查看我的更新。
  • 更新的解构解决方案很棒。虽然我的心有点被它震撼了。为什么const {[prop], ...rest} = obj 不起作用?为什么必须将提取的属性分配给一个新变量(在这种情况下为omit)?
  • 如果属性名不是静态的,也可以是一行:const {[p]: val,...o2} = o;
【解决方案2】:

使用 ES7 对象解构:

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

【讨论】:

  • 如果a 存储在变量const x = 'a' 中会怎样?
  • 没有任何改变。 a 仅指数组的第一个元素。可能是这样的:const { a,b, ...noA } = myObject;console.log(noA); // => {c: 3 }
  • 这是最优雅、最简单的解决方案
  • 如果键是数字,有没有办法做到这一点?
  • 如果我错了,请纠正我,但如果要删除的密钥是动态的(来自变量),例如 const {[keyNameOfA], ...noA} = myObject,这是不可能的
【解决方案3】:

单线解决方案

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

解释:

这是删除特定键的通用箭头函数。第一个参数是要删除的键的名称,第二个参数是要从中删除键的对象。请注意,通过对其进行重组,我们会生成精选结果,然后将其返回。

例子:

let example = { 
  first:"frefrze",
  second:"gergerge",
  third: "gfgfg"
}

console.log(removeKey('third', example))
/*
Object {
  first: "frefrze",
  second: "gergerge"
}
*/

【讨论】:

  • 我希望你能解构这个并解释做了什么。
  • @Billybobbonnet 感谢您提供解释。
  • 超级有用的答案!如果您只需要使用一行,您可以自行调用此匿名函数。 const newObject = ((key, {[key]: _, ...rest}) => rest)('propertyName', originalObject)
【解决方案4】:

添加一些带来性能的香料。在下面查看此线程

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;

还有一个更实际的例子:

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.

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

然后Premature optimization is the root of all evil 将启动。所以你需要注意权衡。需要什么,不需要什么。

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

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

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

v8

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

【讨论】:

  • hmm 2 年后仍然没有发布 lodash 5.0 的迹象!?!
  • 是的。 4 似乎持续很长时间。答案需要更新。在那部分。如果你去那里的主人。并搜索文件。你不会发现省略。另一方面,如果您使用最新版本并下载 zip。你会发现它包括省略。所以省略仍然存在。我没有检查他们是如何构建和设置模块的。我会稍微更新一下答案。上面的链接仍然与答案相关。
【解决方案5】:

如上面 cmets 中所建议的,如果您想扩展它以从您的 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;
    }

【讨论】:

  • 你为什么不在reduce中应用过滤条件,这样你就可以节省一个循环?
  • 感谢@IvoSabev 的提示我的代码中有一些函数,我在其中过滤然后减少但会考虑您的建议以保存循环。
【解决方案6】:

如果您尝试解构,我对从 ESLint 规则标准接受的答案的问题:

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

这 2 个新变量 notNeededalsoNotNeeded 可能会根据您的设置引发警告或错误,因为它们现在未使用。那么,如果未使用,为什么要创建新的变量呢?

我认为你需要真正使用delete函数。

【讨论】:

【解决方案7】:

使用 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}

【讨论】:

    【解决方案8】:

    对于我的代码,我想要一个简短版本的 map() 返回值,但多行/多操作解决方案“丑陋”。关键特性是旧的void(0),它解析为undefined

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

    属性留在对象中:

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

    但是传输框架为我杀死了它(b.c. stringify):

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

    【讨论】:

      【解决方案9】:
      export function deleteKeyFromObject(obj, key) {
        return Object.fromEntries(Object.entries(obj).filter(el => el[0] !== key))
      }
      

      【讨论】:

      • 请不要只发布代码作为答案,还要解释您的代码的作用以及它如何解决问题的问题。带有解释的答案通常更有帮助,质量更高,更有可能吸引投票。
      【解决方案10】:

      我为我写了关于问题的重要功能。该函数将 props 的所有值(不是自身,仅值)、数组等清除为多维的。

      注意:函数清除数组中的元素,数组变成空数组。也许可以添加这种情况以作为可选功能。

      https://gist.github.com/semihkeskindev/d979b169e4ee157503a76b06573ae868

      function clearAllValues(data, byTypeOf = false) {
      
          let clearValuesTypeOf = {
              boolean: false,
              number: 0,
              string: '',
          }
      
          // clears array if data is array
          if (Array.isArray(data)) {
              data = [];
          } else if (typeof data === 'object' && data !== null) {
              // loops object if data is object
              Object.keys(data).forEach((key, index) => {
                  // clears array if property value is array
                  if (Array.isArray(data[key])) {
                      data[key] = [];
                  } else if (typeof data[key] === 'object' && data !== null) {
                      data[key] = this.clearAllValues(data[key], byTypeOf);
                  } else {
                      // clears value by typeof value if second parameter is true
                      if (byTypeOf) {
                          data[key] = clearValuesTypeOf[typeof data[key]];
                      } else {
                          // value changes as null if second parameter is false
                          data[key] = null;
                      }
                  }
              });
          } else {
              if (byTypeOf) {
                  data = clearValuesTypeOf[typeof data];
              } else {
                  data = null;
              }
          }
      
          return data;
      }
      

      这是一个在没有删除道具的情况下清除所有值的示例

      let object = {
          name: 'Semih',
          lastname: 'Keskin',
          brothers: [
              {
                  name: 'Melih Kayra',
                  age: 9,
              }
          ],
          sisters: [],
          hobbies: {
              cycling: true,
              listeningMusic: true,
              running: false,
          }
      }
      
      console.log(object);
      // output before changed: {"name":"Semih","lastname":"Keskin","brothers":[{"name":"Melih Kayra","age":9}],"sisters":[],"hobbies":{"cycling":true,"listeningMusic":true,"running":false}}
      
      let clearObject = clearAllValues(object);
      
      console.log(clearObject);
      // output after changed: {"name":null,"lastname":null,"brothers":[],"sisters":[],"hobbies":{"cycling":null,"listeningMusic":null,"running":null}}
      
      let clearObject2 = clearAllValues(object);
      
      console.log(clearObject2);
      // output after changed by typeof: {"name":"","lastname":"","brothers":[],"sisters":[],"hobbies":{"cycling":false,"listeningMusic":false,"running":false}}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-05-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-02
        • 1970-01-01
        相关资源
        最近更新 更多