Xóa giá trị khỏi đối tượng mà không có đột biến


102

Cách tốt và ngắn gọn để xóa giá trị khỏi một đối tượng tại một khóa cụ thể mà không làm thay đổi đối tượng ban đầu là gì?

Tôi muốn làm điều gì đó như:

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

Tôi biết có rất nhiều thư viện bất biến cho các nhiệm vụ như vậy, nhưng tôi muốn thoát ra mà không có thư viện. Nhưng để làm được điều này, một yêu cầu sẽ là phải có một cách dễ dàng và ngắn gọn có thể được sử dụng trong toàn bộ mã, mà không cần trừu tượng hóa phương thức như một hàm tiện ích.

Ví dụ: để thêm một giá trị, tôi làm như sau:

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

Điều này khá ngắn gọn, dễ nhớ và không cần chức năng tiện ích.

Có một cái gì đó như thế này để loại bỏ một giá trị? ES6 rất được hoan nghênh.

Cảm ơn rât nhiều!


"Xóa giá trị mà không làm thay đổi đối tượng" không có ý nghĩa gì. Bạn không thể xóa khỏi nội dung nào đó và đồng thời giữ nguyên. Những gì bạn thực sự đang làm là tạo một bản sao một phần.
JJJ

Câu trả lời:


226

Cập nhật:

Bạn có thể xóa một thuộc tính khỏi một đối tượng bằng phép gán Hủy cấu trúc phức tạp :

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

Mặc dù vậy, nếu tên thuộc tính bạn muốn xóa là tĩnh, thì bạn có thể xóa nó bằng một lớp lót đơn giản:

let {lastname, ...o2} = o

Cách đơn giản nhất là Hoặc bạn có thể sao chép đối tượng của mình trước khi biến đổi nó:

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

Ngoài ra, bạn có thể sử dụng omitchức năng từ lodashthư viện tiện ích :

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

Nó có sẵn như một phần của gói lodash hoặc dưới dạng gói lodash.omit độc lập .


3
Đó có thực sự là giải pháp đơn giản nhất? Ví dụ: đối với mảng, bạn có thể thực hiện a2 = a1.filter(el => el.id !== id)để bỏ qua một phần tử trong mảng bằng một id cụ thể. Không có điều đó cho một đối tượng?
amann

1
Tôi đồng ý với @amann. Dù có một giải pháp tốt hơn, hoặc ES6 thực sự bỏ lỡ điều gì đó về làm cho JS có thể sử dụng một cách thêm chức năng. Ví dụ. Ruby cókeep_if
Augustin Riedinger

1
@AugustinRiedinger thực sự, có một cách. Xem bản cập nhật của tôi.
Leonid Beschastny

2
Giải pháp tái cấu trúc cập nhật là tuyệt vời. Mặc dù tâm trí của tôi có một chút rung động bởi nó. Tại sao không const {[prop], ...rest} = objhoạt động? Tại sao bạn phải gán thuộc tính đã trích xuất cho một biến mới ( omittrong trường hợp này)?
thankweb

1
@ Antoni4 bạn có thể thêm "bỏ qua" hoặc "bỏ qua" vào no-unused-varsquy tắc của mình , nó chỉ là regex.
adrianmc

35

Với cấu trúc đối tượng ES7:

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

1
điều gì sẽ xảy ra nếu ađược lưu trữ trong một biến const x = 'a'?
FFF

Không có gì thay đổi. a chỉ tham chiếu đến phần tử đầu tiên của mảng. Nó có thể là như thế này: const { a,b, ...noA } = myObject; console.log(noA); // => {c: 3 }
senbon

1
đây là thanh lịch nhất, giải pháp đơn giản
Joe

1
Có cách nào để làm điều này nếu các phím là số không?
Marc Sloth Eastman

25

giải pháp một dòng

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

4

Như được đề xuất trong các nhận xét ở trên nếu bạn muốn mở rộng điều này để xóa nhiều hơn một mục khỏi objecttôi muốn sử dụng của bạn filter. vàreduce

ví dụ

    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
Tại sao bạn không áp dụng điều kiện bộ lọc trong giảm, để bạn có thể tiết kiệm cho mình một vòng lặp?
Ivo Sabev

cảm ơn vì mẹo @IvoSabev Tôi có một vài chức năng trong mã của mình, nơi tôi thực hiện bộ lọc sau đó giảm bớt nhưng sẽ xem xét đề xuất của bạn trong tương lai để lưu vòng lặp.
ak85

4

Để thêm một số gia vị mang lại Hiệu suất. Kiểm tra chủ đề này dưới đây

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

Việc sử dụng toán tử xóa có tác động tiêu cực đến hiệu suất đối với mẫu lớp ẩn V8. Nói chung, bạn không nên sử dụng nó.

Ngoài ra, để loại bỏ các thuộc tính liệt kê của riêng đối tượng, chúng ta có thể tạo một bản sao đối tượng mới mà không có các thuộc tính đó (ví dụ sử dụng lodash):

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

Hoặc thậm chí xác định giá trị thuộc tính thành null hoặc không xác định (hoàn toàn bị bỏ qua khi tuần tự hóa thành JSON):

o.prop = không xác định

Bạn cũng có thể sử dụng cách phá hủy

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

Và một ví dụ thực tế hơn:

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

Như bạn thấy, bạn có thể sử dụng [somePropsVarForDynamicName]: scopeVarNamecú pháp cho tên động. Và bạn có thể đặt tất cả trong dấu ngoặc (khối mới) để phần còn lại sẽ được thu gom sau nó.

Đây là một bài kiểm tra: nhập mô tả hình ảnh ở đây

thi hành:

nhập mô tả hình ảnh ở đây

Hoặc chúng ta có thể đi với một số chức năng như

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;
    }, {});
}

cho chữ viết

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;
    }, {});
}

Sử dụng:

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

a = deleteProps(a, 'propB'); 

// or 

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

Bằng cách này, một đối tượng mới được tạo ra. Và tài sản nhanh chóng của đối tượng được giữ. Cái nào có thể là quan trọng hoặc quan trọng. Nếu ánh xạ và đối tượng sẽ được truy cập nhiều lần.

Ngoài ra, liên kết undefinedcó thể là một cách tốt để đi cùng. Khi bạn có đủ khả năng. Và đối với các phím, bạn cũng có thể kiểm tra giá trị. Ví dụ: để lấy tất cả các khóa hoạt động, bạn làm như sau:

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 không phù hợp với danh sách lớn. Hoặc phát triển theo thời gian với nhiều đạo cụ xuất hiện. Vì việc sử dụng bộ nhớ sẽ tiếp tục tăng và sẽ không bao giờ được xóa sạch. Vì vậy, nó phụ thuộc vào cách sử dụng. Và chỉ tạo một đối tượng mới có vẻ là cách tốt.

Sau đó, ý Premature optimization is the root of all evilchí bắt đầu. Vì vậy, bạn cần phải nhận thức được sự đánh đổi. Và những gì cần thiết và những gì không.

Lưu ý về _.omit () từ lodash

Nó bị xóa khỏi phiên bản 5. Bạn không thể tìm thấy nó trong repo. Và đây là một vấn đề nói về nó.

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

v8

Bạn có thể kiểm tra đây là một bài đọc tốt https://v8.dev/blog/fast-properties


1

Vấn đề của tôi với câu trả lời được chấp nhận, từ một tiêu chuẩn quy tắc ESLint, nếu bạn cố gắng cơ cấu lại:

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

2 biến mới notNeededalsoNotNeededcó thể đưa ra cảnh báo hoặc lỗi tùy thuộc vào thiết lập của bạn vì chúng hiện không được sử dụng. Vậy tại sao phải tạo vars mới nếu không sử dụng?

Tôi nghĩ rằng bạn cần phải sử dụng deletechức năng thực sự.


4
Các no-unused-varsquy tắc thực sự có một tùy chọn ignoreRestSiblingshiện nay để bù đắp trường hợp sử dụng này: eslint.org/docs/rules/no-unused-vars#ignorerestsiblings
AMANN

0

with lodash cloneDeep and delete

(lưu ý: bản sao lodash có thể được sử dụng thay thế cho các đối tượng nông)

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

Đối với mã của tôi, tôi muốn có một phiên bản ngắn cho giá trị trả về của map () nhưng các giải pháp hoạt động multiline / mutli là "xấu xí". Các tính năng chính là cũ void(0)giải quyết undefined.

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

Thuộc tính vẫn nằm trong đối tượng:

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

nhưng khung công tác truyền giết nó cho tôi (bc stringify):

console.log(JSON.stringify(o2)) // {"firstname":"Jane","age":31}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.