JavaScript: Kunci Ganti Nama Objek

299

Apakah ada cara pintar (yaitu dioptimalkan) untuk mengubah nama kunci dalam objek javascript?

Cara yang tidak dioptimalkan adalah:

o[ new_key ] = o[ old_key ];
delete o[ old_key ];
Jean Vincent
sumber
14
Apa yang Anda maksud dengan "dioptimalkan"? Saya tidak berpikir itu bisa lebih singkat dari itu; tidak ada operasi internal "ganti nama".
Runcing
9
Hanya itu yang bisa Anda dapatkan. Saya akan khawatir tentang hal-hal lain dalam aplikasi saya. Dan btw, Anda berurusan dengan objek, bukan array. Tidak ada array asosiatif dalam JavaScript (dalam arti yang ketat).
Felix Kling
3
@ Jean Vincent: Apakah itu lambat?
Felix Kling
8
ini adalah versi yang paling optimal dan mendasar
Livingston Samuel
4
versi Anda adalah yang tercepat di semua peramban modern kecuali safari, contoh uji kasus @ jsperf.com/livi-006
Livingston Samuel

Jawaban:

193

Saya yakin cara paling lengkap untuk melakukan ini adalah:

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

Metode ini memastikan bahwa properti yang diubah namanya berperilaku identik dengan yang asli.

Juga, bagi saya tampaknya kemungkinan untuk membungkus ini menjadi fungsi / metode dan memasukkannya ke dalam Object.prototypetidak relevan dengan pertanyaan Anda.

Valeriu Paloş
sumber
1
Ini adalah solusi hanya-ES5 yang bagus dengan nilai tambah nyata untuk menjaga semua properti. Jadi ini bukan "dioptimalkan" tetapi jelas lebih akurat pada ES5.
Jean Vincent
6
terangkat dengan beberapa skeptisisme menuju writing_things_with_underscores, tetapi tentu saja itu karena orang yang mengajukan pertanyaan. ini tanggapan yang benar menurut saya.
Bent Cardan
3
Dalam hal ini penting, saya percaya penggunaan ES5 khusus ini adalah solusi IE9 dan lebih baru.
Scott Stafford
1
Saya sarankan menambahkan validasi yang ada o.old_key. Mengubah: if (old_key !== new_key)untuk: if (old_key !== new_key && o[old_key])
jose
100

Anda bisa membungkus pekerjaan dalam suatu fungsi dan menugaskannya ke Objectprototipe. Mungkin menggunakan gaya antarmuka yang lancar untuk membuat banyak aliran nama.

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 Spesifik

Saya berharap sintaksnya tidak serumit ini, tetapi sudah pasti bagus memiliki lebih banyak kontrol.

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;
        }
    }
);
Kekacauan Kekacauan
sumber
4
@ChaosPandion menyesal tentang itu, tapi saya benar-benar bosan dengan bug kode JS, saya saat ini sedang menulis Panduan ( github.com/BonsaiDen/JavaScript-Garden ) tentang semua kebiasaan (termasuk yang sekarang telah Anda perbaiki), ini mungkin telah menempatkan saya ke semacam mode kata-kata kasar;) (Dihapus -1 sekarang)
Ivo Wetzel
1
@Ivo - Bisakah Anda menjelaskan mengapa Anda merasa memperpanjang prototipe itu buruk? Prototipe dibuat untuk memperpanjang bayi!
ChaosPandion
5
@ Ivo - Saya setuju bahwa ini berisiko, tetapi tidak ada yang akan menghentikan saya untuk memperpanjang prototipe jika saya merasa itu mengarah pada kode yang lebih ekspresif. Meskipun saya berasal dari pola pikir ECMAScript 5 di mana Anda dapat menandai properti sebagai non-enumerable via Object.defineProperty.
ChaosPandion
3
@Ivo - Jika itu adalah praktik yang tepat untuk selalu menggunakan hasOwnPropertyuntuk memeriksa properti dan dalam for ... inloop, lalu mengapa itu penting jika kita memperluas Object?
David Tang
2
Kode ini berfungsi tetapi peringatannya adalah bahwa jika nama kunci yang baru sudah ada, nilainya akan turun diinjak: jsfiddle.net/ryurage/B7x8x
Brandon Minton
83

Jika Anda bermutasi objek sumber Anda, ES6 dapat melakukannya dalam satu baris.

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

Atau dua baris jika Anda ingin membuat objek baru.

const newObject = {};
delete Object.assign(newObject, o, {[newKey]: o[oldKey] })[oldKey];
nverba
sumber
5
Coba: hapus Object.assign (o, {newKey: o.oldKey}). OldKey; Bekerja dengan baik untuk saya
Tim
2
Mengapa ada penjepit persegi []di sekitar newKey? Solusinya bekerja tanpa itu sebenarnya.
Soumya Kanti
2
Ok - saya mendapat jawaban saya. Ini fitur ES6. Bagi orang lain yang tersandung pada ini seperti saya, ini adalah jawaban yang bagus .
Soumya Kanti
1
Jawaban ini akan lebih kuat jika nama variabel Anda lebih verbose.
lowcrawler
1
Mungkin? Maaf, saya lupa apa yang saya pikirkan dengan komentar awal saya.
lowcrawler
42

Jika seseorang perlu mengganti nama daftar properti:

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

Pemakaian:

const obj = { a: "1", b: "2" };
const newKeys = { a: "A", c: "C" };
const renamedObj = renameKeys(obj, newKeys);
console.log(renamedObj);
// {A:"1", b:"2"}
pomber
sumber
2
jawaban ini harus diterima, bekerja sangat baik untuk kasus penggunaan saya. Saya harus mengonversi respons API yang mengawali Nama Negara dengan string yang tidak perlu. Mengubah Objek menjadi array kunci dan memetakan melalui setiap kunci dengan metode substring dan mengembalikan objek baru dengan kunci dan nilai baru yang diindeks dengan kunci asli
Akin Hwan
1
Ini bagus karena tidak menghapus kunci lama. Menghapus kunci lama akan menghapus kunci sepenuhnya dari objek jika nama kunci tidak berubah. Dalam contoh saya, saya mengkonversi my_object_propertyke myObjectProperty, namun, jika properti saya adalah satu-worded, maka kuncinya telah dihapus. +1
blueprintchris
25

Saya hanya ingin menggunakan ES6(ES2015)caranya!

kita perlu mengikuti perkembangan zaman!

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"}

pintas layar demo

xgqfrms-gildata
sumber
1
Menemukan ini sebagai solusi bersih ketika Anda ingin mengubah objek cacat menjadi yang baru direstrukturisasi.
NJInamdar
5
Bagus, tapi saya pikir const {k1: kA, k2: kB, k3: kC,} = old_obj sudah cukup? tidak perlu menyebar (kecuali jika Anda ingin salinan old_obj).
Hans
@Hans, ya kamu benar. Tetapi dalam situasi ini, saya ingin mengganti nama beberapa kunci, jadi saya harus menyebarkannya.
xgqfrms-gildata
2
Ini membutuhkan kode lebih banyak, akan kurang efisien, dan membuat objek baru. Mengganti nama properti menyiratkan mutasi objek. Lebih baru tidak selalu lebih baik.
Jean Vincent
13

Sebagian besar jawaban di sini gagal mempertahankan urutan pasangan kunci-nilai Objek JS. Jika Anda memiliki bentuk pasangan nilai kunci objek pada layar yang ingin Anda modifikasi, misalnya, penting untuk mempertahankan urutan entri objek.

Cara ES6 untuk mengulang melalui objek JS dan mengganti pasangan nilai kunci dengan pasangan baru dengan nama kunci yang dimodifikasi akan menjadi sesuatu seperti:

let newWordsObject = {};

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

Solusi mempertahankan urutan entri dengan menambahkan entri baru di tempat yang lama.

afalak
sumber
Sempurna, inilah yang saya butuhkan
p0358
12

Anda dapat mencoba 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>

Penny Liu
sumber
Solusi ini berhasil bagi saya, tetapi perhatikan untuk selalu mengembalikan variabel 'kunci'. Contoh: if (key === 'oldKey') {return 'newKey'; } kembali kunci;
TomoMiha
12

Variasi menggunakan operator perusakan dan penyebaran objek:

    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};
Jeff Lowery
sumber
Mungkin Anda bisa menjelaskan apa yang dilakukan dan bagaimana cara kerjanya?
Jon Adams
@ Jeffffowery bagaimana jika itu adalah array objek? Apakah saya harus melakukan foreach atau bisa saya lakukan dengan penyebaran juga?
user3808307
@ user3808307 Anda dapat melakukan spread pada array, dan objek dalam array.
Jeff Lowery
7

Secara pribadi, cara paling efektif untuk mengubah nama kunci pada objek tanpa menerapkan plugins dan roda ekstra berat:

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

object = JSON.parse(str);

Anda juga dapat membungkusnya try-catchjika objek Anda memiliki struktur yang tidak valid. Bekerja dengan sempurna :)

Novitoll
sumber
12
ooops! var object = {b: '123', 'c': 'bbb'}; var str = JSON.stringify (objek); str = str.replace (/ b / g, 'newKey'); str = str.replace (/ c / g, 'newKey2'); object = JSON.parse (str);
BlondinkaBrain
4
Bagaimana ini dibandingkan dengan kecepatan?
mix3d
16
Juga mengalami masalah potensial di suatu tempat di nilai data adalah string yang sama dengan nama kunci lama.
mix3d
4
"Bekerja dengan sempurna" untuk definisi tertentu dari "dengan sempurna". Cobalah untuk mengubah nama 'a'di { a: 1, aa: 2, aaa: 3}atau mencoba untuk mengubah nama objek dengan nilai-nilai yang sesuatu yang lebih dari string atau angka atau boolean, atau mencoba dengan referensi melingkar.
rsp
1
jika objek berisi fungsi atau objek tanggal, ini tidak akan berfungsi
ikhsan
7

Untuk menambahkan awalan ke setiap tombol:

const obj = {foo: 'bar'}

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

// altObj = {'x-foo': 'bar'}
piotr_cz
sumber
5

Saya akan melakukan sesuatu seperti ini:

function renameKeys(dict, keyMap) {
  return _.reduce(dict, function(newDict, val, oldKey) {
    var newKey = keyMap[oldKey] || oldKey
    newDict[newKey] = val 
    return newDict
  }, {})
}
tldr
sumber
3
Membuat versi terbaru dari solusi Anda yang tidak mengubah kunci statis menjadi 'tidak terdefinisi' di sini: gist.github.com/diasdavid/f8997fb0bdf4dc1c3543 Namun demikian, masih belum mengatasi masalah ingin memetakan kembali kunci di beberapa level JSON objek (remaping kunci bersarang), ide?
David Dias
4

Saya akan mengatakan bahwa akan lebih baik dari sudut pandang konseptual untuk meninggalkan objek lama (yang dari layanan web) apa adanya, dan menempatkan nilai-nilai yang Anda butuhkan dalam objek baru. Saya berasumsi Anda mengekstraksi bidang tertentu pada satu titik atau yang lain, jika tidak pada klien, maka setidaknya di server. Fakta bahwa Anda memilih untuk menggunakan nama bidang yang sama dengan yang dari layanan web, hanya huruf kecil, tidak benar-benar mengubah ini. Jadi, saya menyarankan untuk melakukan sesuatu seperti ini:

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

Tentu saja, saya membuat segala macam asumsi di sini, yang mungkin tidak benar. Jika ini tidak berlaku untuk Anda, atau jika terlalu lambat (bukan? Saya belum menguji, tapi saya membayangkan perbedaannya semakin kecil dengan meningkatnya jumlah bidang), abaikan semua ini :)

Jika Anda tidak ingin melakukan ini, dan Anda hanya perlu mendukung browser tertentu, Anda juga dapat menggunakan getter baru untuk juga mengembalikan "huruf besar (bidang)": lihat http://robertnyman.com/2009/05/28 / getters-and-setters-with-javascript-code-samples-and-demo / dan tautan pada halaman itu untuk informasi lebih lanjut.

EDIT:

Luar biasa, ini juga hampir dua kali lebih cepat, setidaknya pada FF3.5 saya di tempat kerja. Lihat: http://jsperf.com/spiny001

Spiny Norman
sumber
Terima kasih banyak atas upaya Anda tetapi use case tidak memanipulasi objek statis karena struktur dan kedalamannya tidak diketahui. Jadi, jika mungkin, saya ingin tetap pada ruang lingkup asli pertanyaan.
Jean Vincent
4

Meskipun ini tidak memberikan solusi yang lebih baik untuk mengganti nama kunci, ini menyediakan cara ES6 yang cepat dan mudah untuk mengganti nama semua kunci dalam suatu objek tanpa mengubah data yang dikandungnya.

let b = {a: ["1"], b:["2"]};
Object.keys(b).map(id => {
  b[`root_${id}`] = [...b[id]];
  delete b[id];
});
console.log(b);
Tudor Morar
sumber
3
Silakan gunakan tautan edit untuk menjelaskan cara kerja kode ini dan jangan hanya memberikan kode, karena penjelasan lebih mungkin membantu pembaca di masa mendatang. Lihat juga Cara Menjawab . sumber
Jed Fox
Saya setuju. Itu tidak menjawab pertanyaan secara khusus tetapi itu membantu saya menyelesaikan pekerjaan sebagai solusi untuk mengganti nama semua kunci dalam objek. Mungkin saya ketinggalan topik ..
Tudor Morar
3

Beberapa solusi yang tercantum di halaman ini memiliki beberapa efek samping:

  1. mempengaruhi posisi kunci pada objek, menambahkannya ke bawah (jika ini penting bagi Anda)
  2. tidak akan berfungsi di IE9 + (sekali lagi, jika ini penting bagi Anda)

Berikut adalah solusi yang menjaga posisi kunci di tempat yang sama dan kompatibel di IE9 +, tetapi harus membuat objek baru dan mungkin bukan solusi tercepat:

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;
}

Harap dicatat: IE9 mungkin tidak mendukung untukEach dalam mode ketat

Jasdeep Khalsa
sumber
2

Berikut ini adalah contoh untuk membuat objek baru dengan tombol berganti nama.

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

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

Namun cara lain dengan metode REDUCE paling kuat .

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);

SubBU
sumber
1

Ini adalah modifikasi kecil yang saya buat untuk fungsi pomber; Untuk dapat mengambil Array Objek alih-alih objek saja dan Anda juga dapat mengaktifkan indeks. "Tombol" juga dapat ditetapkan oleh array

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;
}

uji

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);
Jasp402
sumber
1

Cara Anda dioptimalkan, menurut saya. Tetapi Anda akan berakhir dengan kunci yang dipesan ulang. Kunci yang baru dibuat akan ditambahkan di bagian akhir. Saya tahu Anda tidak boleh bergantung pada urutan kunci, tetapi jika Anda harus melestarikannya, Anda harus memeriksa semua kunci dan membuat objek baru satu per satu, mengganti kunci yang dimaksud selama proses itu.

Seperti ini:

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;
Tomas M
sumber
0
  • Anda dapat menggunakan utilitas untuk menangani ini.
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' };
Muhammed Moussa
sumber
0

coba saja di editor favorit Anda <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
}
Pablo
sumber