JavaScript: Khóa đổi tên đối tượng


299

Có cách nào thông minh (tức là tối ưu hóa) để đổi tên khóa trong đối tượng javascript không?

Một cách không được tối ưu hóa sẽ là:

o[ new_key ] = o[ old_key ];
delete o[ old_key ];

14
"Tối ưu hóa" nghĩa là gì? Tôi không nghĩ rằng nó có thể nhận được nhiều điều ngắn gọn hơn thế; không có "đổi tên" hoạt động tích hợp.
Mũi nhọn

9
Đó là tất cả những gì bạn có thể nhận được. Tôi sẽ lo lắng về những thứ khác trong ứng dụng của tôi. Và btw, bạn đang xử lý các đối tượng, không phải mảng. Không có mảng kết hợp trong JavaScript (theo nghĩa chặt chẽ).
Felix Kling

3
@Jean Vincent: Có chậm không?
Felix Kling

8
đây là phiên bản cơ bản và tối ưu nhất
Livingston Samuel

4
phiên bản của bạn là nhanh nhất trong tất cả các trình duyệt hiện đại, ngoại trừ safari, trường hợp thử nghiệm mẫu @ jsperf.com/livi-006
Livingston Samuel

Câu trả lời:


193

Cách tốt nhất (và chính xác) để làm điều này sẽ là, tôi tin:

if (old_key !== new_key) {
    Object.defineProperty(o, new_key,
        Object.getOwnPropertyDescriptor(o, old_key));
    delete o[old_key];
}

Phương pháp này đảm bảo rằng thuộc tính được đổi tên hoạt động giống hệt với tài sản ban đầu.

Ngoài ra, đối với tôi, khả năng bao bọc nó thành một hàm / phương thức và đưa nó vào Object.prototype là không liên quan đến câu hỏi của bạn.


1
Đây là một giải pháp chỉ dành cho ES5 với điểm cộng thực sự là bảo toàn tất cả các thuộc tính. Vì vậy, đây không phải là "tối ưu hóa" nhưng chắc chắn chính xác hơn trên ES5.
Jean Vincent

6
được nâng cao với một số sự hoài nghi đối với Writing_things_with_underscores, nhưng tất nhiên đó là do người đặt câu hỏi. Đây là phản ứng chính xác trong quan điểm của tôi.
Bent Cardan

3
Trong trường hợp có vấn đề, tôi tin rằng việc sử dụng ES5 cụ thể này là một giải pháp IE9 và mới hơn.
Scott Stafford

1
Tôi đề nghị thêm một xác nhận rằng o.old_key tồn tại. Thay đổi: if (old_key !== new_key)đến: if (old_key !== new_key && o[old_key])
jose

100

Bạn có thể gói công việc trong một hàm và gán nó cho Objectnguyên mẫu. Có thể sử dụng kiểu giao diện trôi chảy để thực hiện nhiều lần đổi tên.

Object.prototype.renameProperty = function (oldName, newName) {
     // Do nothing if the names are the same
     if (oldName === newName) {
         return this;
     }
    // Check for the old property name to avoid a ReferenceError in strict mode.
    if (this.hasOwnProperty(oldName)) {
        this[newName] = this[oldName];
        delete this[oldName];
    }
    return this;
};

ECMAScript 5 Cụ thể

Tôi ước cú pháp không phức tạp nhưng chắc chắn sẽ có nhiều quyền kiểm soát hơn.

Object.defineProperty(
    Object.prototype, 
    'renameProperty',
    {
        writable : false, // Cannot alter this property
        enumerable : false, // Will not show up in a for-in loop.
        configurable : false, // Cannot be deleted via the delete operator
        value : function (oldName, newName) {
            // Do nothing if the names are the same
            if (oldName === newName) {
                return this;
            }
            // Check for the old property name to 
            // avoid a ReferenceError in strict mode.
            if (this.hasOwnProperty(oldName)) {
                this[newName] = this[oldName];
                delete this[oldName];
            }
            return this;
        }
    }
);

4
@ChaosPandion xin lỗi về điều đó, nhưng tôi thực sự mệt mỏi của lỗi cưỡi mã JS, Tôi hiện đang viết một hướng dẫn ( github.com/BonsaiDen/JavaScript-Garden ) về tất cả những khuyết tật (trong đó có một bây giờ bạn đã cố định), điều này có thể đã đưa tôi vào một số loại chế độ rant;) (Đã xóa -1 ngay bây giờ)
Ivo Wetzel

1
@Ivo - Bạn có thể giải thích lý do tại sao bạn cảm thấy việc mở rộng nguyên mẫu là xấu? Nguyên mẫu đã được thực hiện để mở rộng em bé!
ChaosPandion

5
@Ivo - Tôi đồng ý rằng đó là rủi ro, nhưng không có gì ngăn cản tôi mở rộng nguyên mẫu nếu tôi cảm thấy nó dẫn đến mã biểu cảm hơn. Mặc dù tôi đến từ một tư duy ECMAScript 5 nơi bạn có thể đánh dấu một tài sản là không thể đếm được thông qua Object.defineProperty.
ChaosPandion

3
@Ivo - Nếu đó là cách thực hành đúng để luôn luôn sử dụng hasOwnPropertyđể kiểm tra các thuộc tính và trong for ... incác vòng lặp, thì tại sao chúng ta lại mở rộng Object?
David Tang

2
Mã này hoạt động nhưng cảnh báo là nếu tên khóa mới đã tồn tại, giá trị của nó sẽ bị giảm xuống: jsfiddle.net/ryurage/B7x8x
Brandon Minton

83

Nếu bạn đang thay đổi đối tượng nguồn của mình, ES6 có thể thực hiện điều đó trong một dòng.

delete Object.assign(o, {[newKey]: o[oldKey] })[oldKey];

Hoặc hai dòng nếu bạn muốn tạo một đối tượng mới.

const newObject = {};
delete Object.assign(newObject, o, {[newKey]: o[oldKey] })[oldKey];

5
Hãy thử: xóa Object.assign (o, {newKey: o.oldKey}). OldKey; Làm việc tốt với tôi
Tim

2
Tại sao có nẹp vuông []xung quanh newKey? Các giải pháp hoạt động mà không thực sự đó.
Soumya Kanti

2
Ok - tôi đã có câu trả lời của mình. Đây là tính năng ES6. Đối với bất cứ ai khác vấp phải điều này như tôi, đây là một câu trả lời tốt .
Soumya Kanti

1
Câu trả lời này sẽ mạnh hơn nếu tên biến của bạn dài dòng hơn.
lowcrawler

1
Có lẽ? Xin lỗi, tôi quên những gì tôi đã nghĩ với nhận xét ban đầu của tôi.
lowcrawler

42

Trong trường hợp ai đó cần đổi tên danh sách tài sản:

function renameKeys(obj, newKeys) {
  const keyValues = Object.keys(obj).map(key => {
    const newKey = newKeys[key] || key;
    return { [newKey]: obj[key] };
  });
  return Object.assign({}, ...keyValues);
}

Sử dụng:

const obj = { a: "1", b: "2" };
const newKeys = { a: "A", c: "C" };
const renamedObj = renameKeys(obj, newKeys);
console.log(renamedObj);
// {A:"1", b:"2"}

2
câu trả lời này nên được chấp nhận, hoạt động rất tốt cho trường hợp sử dụng của tôi. Tôi đã phải chuyển đổi một phản hồi API có tiền tố Tên quốc gia với chuỗi không cần thiết. Biến đối tượng thành mảng các khóa và ánh xạ qua từng khóa bằng phương thức chuỗi con và trả về một đối tượng mới với khóa mới và giá trị được lập chỉ mục bởi khóa gốc
Akin Hwan

1
Điều này là tốt vì nó không xóa khóa cũ. Xóa khóa cũ sẽ xóa hoàn toàn khóa khỏi đối tượng nếu tên khóa không thay đổi. Trong ví dụ của tôi, tôi đã chuyển đổi my_object_propertysang myObjectProperty, tuy nhiên, nếu tài sản của tôi là một từ, thì khóa đã bị xóa. +1
blueprintchris

25

Tôi chỉ muốn sử dụng ES6(ES2015)cách!

chúng ta cần theo kịp thời đại!

const old_obj = {
    k1: `111`,
    k2: `222`,
    k3: `333`
};
console.log(`old_obj =\n`, old_obj);
// {k1: "111", k2: "222", k3: "333"}


/**
 * @author xgqfrms
 * @description ES6 ...spread & Destructuring Assignment
 */

const {
    k1: kA, 
    k2: kB, 
    k3: kC,
} = {...old_obj}

console.log(`kA = ${kA},`, `kB = ${kB},`, `kC = ${kC}\n`);
// kA = 111, kB = 222, kC = 333

const new_obj = Object.assign(
    {},
    {
        kA,
        kB,
        kC
    }
);

console.log(`new_obj =\n`, new_obj);
// {kA: "111", kB: "222", kC: "333"}

lối tắt màn hình demo


1
Tìm thấy một trong những giải pháp sạch hơn khi bạn muốn chuyển đổi một đối tượng không đúng định dạng thành một đối tượng tái cấu trúc hoàn toàn mới.
NJInamdar

5
Tốt, nhưng tôi nghĩ const {k1: kA, k2: kB, k3: kC,} = old_obj là đủ? không cần phải truyền bá (trừ khi bạn muốn có một bản sao của old_obj).
Hans

@Hans, vâng bạn nói đúng. Nhưng trong tình huống này, tôi muốn đổi tên một số khóa, vì vậy tôi nên trải rộng chúng.
xgqfrms-gildata

2
Điều này đòi hỏi nhiều mã hơn, sẽ kém hiệu quả hơn và tạo ra một đối tượng mới. Đổi tên một tài sản ngụ ý làm biến đổi đối tượng. Mới hơn không phải lúc nào cũng tốt hơn.
Jean Vincent


13

Hầu hết các câu trả lời ở đây không duy trì thứ tự cặp giá trị khóa-đối tượng JS. Ví dụ, nếu bạn có một dạng cặp khóa-giá trị đối tượng trên màn hình mà bạn muốn sửa đổi, điều quan trọng là phải giữ nguyên thứ tự của các mục nhập đối tượng.

Cách ES6 lặp qua đối tượng JS và thay thế cặp khóa-giá trị bằng cặp mới bằng tên khóa đã sửa đổi sẽ giống như:

let newWordsObject = {};

Object.keys(oldObject).forEach(key => {
  if (key === oldKey) {
    let newPair = { [newKey]: oldObject[oldKey] };
    newWordsObject = { ...newWordsObject, ...newPair }
  } else {
    newWordsObject = { ...newWordsObject, [key]: oldObject[key] }
  }
});

Giải pháp duy trì thứ tự của các mục bằng cách thêm mục mới vào vị trí của mục cũ.


Hoàn hảo, đây là những gì tôi cần
p0353

12

Bạn có thể thử lodash _.mapKeys.

var user = {
  name: "Andrew",
  id: 25,
  reported: false
};

var renamed = _.mapKeys(user, function(value, key) {
  return key + "_" + user.id;
});

console.log(renamed);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>


Giải pháp này hiệu quả với tôi, nhưng chú ý luôn trả về biến 'khóa'. Ví dụ: if (key === 'oldKey') {return 'newKey'; } phím trả về;
TomoMiha

12

Một biến thể sử dụng toán tử phá hủy và truyền bá đối tượng:

    const old_obj = {
        k1: `111`,
        k2: `222`,
        k3: `333`
    };


// destructuring, with renaming. The variable 'rest' will hold those values not assigned to kA, kB, or kC.
    const {
        k1: kA, 
        k2: kB, 
        k3: kC,
        ...rest
    } = old_obj;


// now create a new object, with the renamed properties kA, kB, kC; 
// spread the remaining original properties in the 'rest' variable
const newObj = {kA, kB, kC, ...rest};

Có lẽ bạn có thể giải thích những gì nó đang làm và làm thế nào nó hoạt động?
Jon Adams

@JeffLowery nếu đó là một mảng các đối tượng thì sao? Tôi có phải làm một bài giảng hay tôi cũng có thể làm với sự lây lan?
3808307

@ user3808307 Bạn có thể thực hiện một trải trên một mảng và các đối tượng trong mảng.
Jeff Lowery

7

Cá nhân, cách hiệu quả nhất để đổi tên các khóa trong đối tượng mà không cần thực hiện thêm các plugin và bánh xe nặng:

var str = JSON.stringify(object);
str = str.replace(/oldKey/g, 'newKey');
str = str.replace(/oldKey2/g, 'newKey2');

object = JSON.parse(str);

Bạn cũng có thể bọc nó try-catchnếu đối tượng của bạn có cấu trúc không hợp lệ. Hoạt động hoàn hảo :)


12
ôi! đối tượng var = {b: '123', 'c': 'bbb'}; var str = JSON.opesify (object); str = str.replace (/ b / g, 'newKey'); str = str.replace (/ c / g, 'newKey2'); object = JSON.parse (str);
BlondinkaBrain 16/2/2016

4
Làm thế nào để so sánh tốc độ này?
mix3d

16
Cũng gặp phải một vấn đề tiềm ẩn ở đâu đó trong bảng dữ liệu là cùng một chuỗi với tên khóa cũ.
mix3d

4
"Hoạt động hoàn hảo" cho các định nghĩa nhất định về "hoàn hảo". Cố gắng đổi tên 'a'trong { a: 1, aa: 2, aaa: 3}hoặc cố gắng đổi tên một đối tượng với các giá trị là bất kỳ thứ gì ngoài chuỗi hoặc số hoặc booleans hoặc thử với các tham chiếu tròn.
rsp

1
nếu đối tượng chứa hàm hoặc đối tượng ngày, điều này sẽ không hoạt động
ikhsan

7

Để thêm tiền tố vào mỗi khóa:

const obj = {foo: 'bar'}

const altObj = Object.fromEntries(
  Object.entries(obj).map(([key, value]) => 
    // Modify key here
    [`x-${key}`, value]
  )
)

// altObj = {'x-foo': 'bar'}

5

Tôi sẽ làm một cái gì đó như thế này:

function renameKeys(dict, keyMap) {
  return _.reduce(dict, function(newDict, val, oldKey) {
    var newKey = keyMap[oldKey] || oldKey
    newDict[newKey] = val 
    return newDict
  }, {})
}

3
Đã tạo phiên bản cập nhật cho giải pháp của bạn mà không chuyển đổi các khóa tĩnh thành 'không xác định' tại đây: gist.github.com/diasdavid/f8997fb0bdf4dc1c3543 Tuy nhiên, vẫn không gặp phải vấn đề muốn sắp xếp lại các khóa ở nhiều cấp độ của JSON đối tượng (sửa lỗi lồng nhau), ý tưởng?
David Dias

4

Tôi sẽ nói rằng sẽ tốt hơn nếu theo quan điểm khái niệm là chỉ để lại đối tượng cũ (đối tượng từ dịch vụ web), và đặt các giá trị bạn cần vào một đối tượng mới. Tôi giả sử rằng bạn đang trích xuất các trường cụ thể tại điểm này hay điểm khác, nếu không phải trên máy khách, thì ít nhất là trên máy chủ. Thực tế là bạn đã chọn sử dụng tên trường giống như tên từ dịch vụ web, chỉ chữ thường, không thực sự thay đổi điều này. Vì vậy, tôi khuyên bạn nên làm một cái gì đó như thế này:

var myObj = {
    field1: theirObj.FIELD1, 
    field2: theirObj.FIELD2,
    (etc)
}

Tất nhiên, tôi đang đưa ra tất cả các giả định ở đây, điều này có thể không đúng. Nếu điều này không áp dụng cho bạn hoặc nếu nó quá chậm (phải không? Tôi đã thử nghiệm, nhưng tôi tưởng tượng sự khác biệt sẽ nhỏ hơn khi số lượng trường tăng lên), xin vui lòng bỏ qua tất cả điều này :)

Nếu bạn không muốn làm điều này và bạn chỉ phải hỗ trợ các trình duyệt cụ thể, bạn cũng có thể sử dụng các getters mới để trả về "chữ hoa (trường)": xem http://robertnyman.com/2009/05/28 / getters-and-setters-with-javascript-code-samples-and-demos / và các liên kết trên trang đó để biết thêm thông tin.

BIÊN TẬP:

Thật đáng kinh ngạc, điều này cũng nhanh gần gấp đôi, ít nhất là trên FF3.5 của tôi khi làm việc. Xem: http://jsperf.com/spiny001


Cảm ơn bạn rất nhiều vì những nỗ lực của bạn nhưng trường hợp sử dụng không thao túng các đối tượng tĩnh vì cấu trúc và độ sâu của chúng không được xác định. Vì vậy, nếu có thể tôi muốn bám vào phạm vi ban đầu của câu hỏi.
Jean Vincent

4

Mặc dù điều này không chính xác đưa ra một giải pháp tốt hơn để đổi tên khóa, nhưng nó cung cấp cách ES6 nhanh chóng và dễ dàng để đổi tên tất cả các khóa trong một đối tượng trong khi không làm thay đổi dữ liệu mà chúng chứa.

let b = {a: ["1"], b:["2"]};
Object.keys(b).map(id => {
  b[`root_${id}`] = [...b[id]];
  delete b[id];
});
console.log(b);

3
Vui lòng sử dụng liên kết chỉnh sửa để giải thích cách mã này hoạt động và không chỉ cung cấp mã, vì một lời giải thích có nhiều khả năng giúp người đọc trong tương lai. Xem thêm Cách trả lời . nguồn
Jed Fox

Tôi đồng ý. Nó không trả lời cụ thể câu hỏi nhưng nó giúp tôi hoàn thành công việc như một giải pháp để đổi tên tất cả các khóa trong đối tượng. Có lẽ tôi đã bỏ lỡ chủ đề ..
Tudor Morar

3

Một số giải pháp được liệt kê trên trang này có một số tác dụng phụ:

  1. ảnh hưởng đến vị trí của khóa trong đối tượng, thêm nó vào dưới cùng (nếu điều này quan trọng với bạn)
  2. sẽ không hoạt động trong IE9 + (một lần nữa, nếu điều này quan trọng với bạn)

Đây là một giải pháp giữ vị trí của khóa ở cùng một vị trí và tương thích trong IE9 +, nhưng phải tạo một đối tượng mới và có thể không phải là giải pháp nhanh nhất:

function renameObjectKey(oldObj, oldName, newName) {
    const newObj = {};

    Object.keys(oldObj).forEach(key => {
        const value = oldObj[key];

        if (key === oldName) {
            newObj[newName] = value;
        } else {
            newObj[key] = value;
        }
    });

    return newObj;
}

Xin lưu ý: IE9 có thể không hỗ trợ forEach ở chế độ nghiêm ngặt


2

Dưới đây là một ví dụ để tạo một đối tượng mới với các khóa được đổi tên.

let x = { id: "checkout", name: "git checkout", description: "checkout repository" };

let renamed = Object.entries(x).reduce((u, [n, v]) => {
  u[`__${n}`] = v;
  return u;
}, {});

2

Một cách khác với phương pháp GIẢM GIÁ mạnh mẽ nhất .

data = {key1: "value1", key2: "value2", key3: "value3"}; 

keyMap = {key1: "firstkey", key2: "secondkey", key3: "thirdkey"};

mappedData = Object.keys(keyMap).reduce((obj,k) => Object.assign(obj, { [keyMap[k]]: data[k] }),{});

console.log(mappedData);


1

Đây là một sửa đổi nhỏ mà tôi đã thực hiện cho chức năng của pomber; Để có thể lấy Mảng đối tượng thay vì một đối tượng và bạn cũng có thể kích hoạt chỉ mục. cũng có thể gán "Khóa" bằng một mảng

function renameKeys(arrayObject, newKeys, index = false) {
    let newArray = [];
    arrayObject.forEach((obj,item)=>{
        const keyValues = Object.keys(obj).map((key,i) => {
            return {[newKeys[i] || key]:obj[key]}
        });
        let id = (index) ? {'ID':item} : {}; 
        newArray.push(Object.assign(id, ...keyValues));
    });
    return newArray;
}

kiểm tra

const obj = [{ a: "1", b: "2" }, { a: "5", b: "4" } ,{ a: "3", b: "0" }];
const newKeys = ["A","C"];
const renamedObj = renameKeys(obj, newKeys);
console.log(renamedObj);

1

Cách của bạn được tối ưu hóa, theo ý kiến ​​của tôi. Nhưng bạn sẽ kết thúc với các phím được sắp xếp lại. Khóa mới được tạo sẽ được thêm vào cuối. Tôi biết bạn không bao giờ nên dựa vào thứ tự khóa, nhưng nếu bạn cần giữ nó, bạn sẽ cần phải đi qua tất cả các khóa và xây dựng từng đối tượng mới, thay thế khóa trong câu hỏi trong quá trình đó.

Như thế này:

var new_o={};
for (var i in o)
{
   if (i==old_key) new_o[new_key]=o[old_key];
   else new_o[i]=o[i];
}
o=new_o;

0
  • Bạn có thể sử dụng một tiện ích để xử lý này.
npm i paix
import { paix } from 'paix';

const source_object = { FirstName: "Jhon", LastName: "Doe", Ignored: true };
const replacement = { FirstName: 'first_name', LastName: 'last_name' };
const modified_object = paix(source_object, replacement);

console.log(modified_object);
// { Ignored: true, first_name: 'Jhon', last_name: 'Doe' };

0

chỉ cần thử nó trong trình soạn thảo yêu thích của bạn <3

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

const OLD_KEY = 1
const NEW_KEY = 10

const { [OLD_KEY]: replaceByKey, ...rest } = obj
const new_obj = {
  ...rest,
  [NEW_KEY]: replaceByKey
}
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.