JavaScript: ключ перейменування об’єкта


299

Чи є розумний (тобто оптимізований) спосіб перейменувати ключ в об’єкті JavaScript?

Неоптимізованим способом буде:

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

14
Що ви маєте на увазі під оптимізованим? Я не думаю, що це може стати більш стислим, ніж це; немає вбудованої операції "перейменування".
Поні

9
Це все, що можна отримати. Я б переймався іншими речами у своїй заяві. І btw, ви маєте справу з об'єктами, а не з масивами. У JavaScript немає асоціативних масивів (у строгому розумінні).
Фелікс Клінг

3
@Jean Vincent: Це так повільно?
Фелікс Клінг

8
це найбільш оптимізована та основна версія
Лівінгстон Самуель

4
Ваша версія є найшвидшою у всіх сучасних браузерах, крім сафарі, зразок тестового випадку @ jsperf.com/livi-006
Livingston Samuel

Відповіді:


193

Я вважаю, що найбільш повним (і правильним) способом цього є:

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

Цей метод забезпечує, що перейменований властивість поводиться однаково з оригінальним.

Крім того, мені здається, що можливість увімкнути це у функцію / метод і вкласти це Object.prototypeне має значення стосовно вашого питання.


1
Це приємне рішення лише для ES5 з реальним плюсом збереження всіх властивостей. Таким чином, це не "оптимізовано", але безумовно точніше на ES5.
Жан Вінсент

6
з певним скептицизмом ставиться до write_things_with_underscores, але, звичайно, це було пов’язано з тим, хто задає питання. це правильна відповідь на мій погляд.
Бент Кардан

3
У випадку, якщо це має значення, я вважаю, що саме це використання ES5 - це рішення IE9 і новіших.
Скотт Стаффорд

1
Я пропоную додати валідацію, що існує o.old_key. Зміна: if (old_key !== new_key)в: if (old_key !== new_key && o[old_key])
Хосе

100

Ви можете зафіксувати роботу у функції та призначити її Objectпрототипу. Можливо, використовуйте вільний стиль інтерфейсу, щоб зробити кілька потоків перейменованими.

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 Специфічний

Я хотів би, щоб синтаксис був не таким складним, але, безумовно, приємно мати більше контролю.

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 вибачте з цього приводу, але я дуже втомився від JS-коду, який позбавлений помилок, я зараз пишу Керівництво ( github.com/BonsaiDen/JavaScript-Garden ) про всі примхи (включаючи ту, яку ви тепер виправили), це, можливо, ввело мене в якийсь режим гніту;) (Вилучено -1 зараз)
Іво Ветцел

1
@Ivo - Чи можете ви пояснити, чому ви вважаєте, що продовження прототипу погано? Прототипи були зроблені для розширення дитини!
ChaosPandion

5
@ Ivo - Я погоджуюся, що це ризиковано, але немає нічого, що не заважатиме мені розширити прототип, якщо я відчув, що це призведе до більш виразного коду. Хоча я виходжу з набору мислення ECMAScript 5, де ви можете позначити властивість як нечисленну через Object.defineProperty.
ChaosPandion

3
@Ivo - Якщо це правильна практика завжди використовувати hasOwnPropertyдля перевірки властивостей і в for ... inциклі, то чому це має значення, якщо ми розширюємо Object?
Девід Тан

2
Цей код працює, але застереження полягає в тому, що якщо нове ім'я ключа вже існує, його значення знизиться: jsfiddle.net/ryurage/B7x8x
Брендон Мінтон

83

Якщо ви мутуєте свій вихідний об’єкт, ES6 може зробити це в одному рядку.

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

Або два рядки, якщо ви хочете створити новий об’єкт.

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

5
Спробуйте: видалити Object.assign (o, {newKey: o.oldKey}). OldKey; Добре працював для мене
Тім

2
Чому квадрат дужка []навколо newKey? Рішення працює без цього насправді.
Soumya Kanti

2
Гаразд - я отримав свою відповідь. Це особливість ES6. Якщо хтось натрапив на таке, як я, ось хороша відповідь .
Сум’я Канті

1
Ця відповідь була б сильнішою, якби ваші назви змінних були більш багатослівними.
низькокрилий

1
Можливо? Вибачте, я забуваю, про що думав з первинним коментарем.
lowcrawler

42

Якщо комусь потрібно перейменувати список об’єктів:

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

Використання:

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
це слід прийняти у відповідь, чудово спрацювало для мого випадку використання. Мені довелося конвертувати відповідь API, яка вказувала зайві рядки назви країн. Перетворив Об'єкт у масив ключів і картографував кожну клавішу методом підрядки та повернув новий об’єкт з новим ключем та значенням, індексованим оригінальним ключем
Акін Хван

1
Це добре, оскільки він не видаляє старий ключ. Видалення старого ключа повністю видалить ключ з об'єкта, якщо ім'я ключа не змінилося. У моєму прикладі, я перетворення my_object_propertyв myObjectProperty, однак, якщо моєї власність була однією сформульованим, то ключ був видалений. +1
план

25

Я хотів би просто використовувати ES6(ES2015)спосіб!

нам потрібно йти в ногу з часом!

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

ярлик демонстраційного екрана


1
Знайшли це як більш чисте рішення, коли потрібно перетворити неправильно сформований об’єкт у абсолютно новий реструктуризований.
NJInamdar

5
Хороший, але я думаю, що const {k1: kA, k2: kB, k3: kC,} = old_obj є достатнім? не потрібно поширювати (якщо ви не хочете копії old_obj).
Ганс

@Hans, так, ти маєш рацію. Але в цій ситуації я хочу перейменувати деякі ключі, тому мені слід їх поширити.
xgqfrms-gildata

2
Для цього потрібно набагато більше коду, буде менш ефективним і створюється новий об’єкт. Перейменування властивості передбачає мутування об'єкта. Новіше не завжди краще.
Жан Вінсент

16

Якщо ви не хочете мутувати свої дані, врахуйте цю функцію ...

renameProp = (oldProp, newProp, {[oldProp]:old, ...others}) => ({
    [newProp]: old,
    ...others
})

Ретельне пояснення Yazeed Bzadough https://medium.com/front-end-hacking/immutably-rename-object-keys-in-javascript-5f6353c7b6dd



Це найкраще рішення. Але вам потрібно трохи зрозуміти про оператор розповсюдження.
франкофон

13

Більшість відповідей тут не підтримують порядок пар ключ-значення JS Object. Якщо у вас на екрані є форма об'єктних пар ключ-значення, яку ви хочете змінити, наприклад, важливо зберегти порядок записів об'єктів.

Спосіб перегляду циклу через об’єкт JS та заміна пари ключ-значення на нову пару з модифікованим іменем ключа буде чимось таким:

let newWordsObject = {};

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

Рішення зберігає порядок записів, додаючи новий запис замість старого.


Ідеально, це те, що мені було потрібно
p0358

12

Можна спробувати лодаш _.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>


Це рішення працювало для мене, але зверніть увагу, щоб завжди повертати змінну 'key'. Приклад: if (key === 'oldKey') {return 'newKey'; } ключ повернення;
TomoMiha

12

Варіант з використанням оператора руйнування об'єкта та оператора розповсюдження:

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

Можливо, ви могли б пояснити, що це робить і як це працює?
Джон Адамс

@JeffLowery, що робити, якщо це масив об’єктів? Чи потрібно робити передчуття чи я теж можу поширюватися?
користувач3808307

@ user3808307 Ви можете виконати розворот на масиві та об'єктах у масиві.
Джефф Лоуні

7

Особисто найефективніший спосіб перейменувати ключі в об'єкті без використання надмірних плагінів та коліс:

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

object = JSON.parse(str);

Ви також можете обернути його, try-catchякщо ваш об'єкт має неправильну структуру. Відмінно працює :)


12
ooops! var object = {b: '123', 'c': 'bbb'}; var str = JSON.stringify (об’єкт); str = str.replace (/ b / g, 'newKey'); str = str.replace (/ c / g, 'newKey2'); object = JSON.parse (str);
BlondinkaBrain

4
Як це порівнюється за швидкістю?
mix3d

16
Також наштовхується на потенційну проблему десь у значеннях даних - це той самий рядок, що і стара назва ключа.
mix3d

4
"Відмінно працює" для певних визначень "ідеально". Спробуйте перейменувати 'a'в { a: 1, aa: 2, aaa: 3}або спробувати перейменувати об'єкт зі значеннями, які нічого більше рядків або чисел або булеві, або спробувати з циклічними посиланнями.
rsp

1
якщо об’єкт містить об’єкт функції або дати, це не працюватиме
ikhsan

7

Щоб додати префікс до кожної клавіші:

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

Я б зробив щось подібне:

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

3
Створено оновлену версію свого рішення, яка не перетворює статичні ключі на "невизначені" тут: gist.github.com/diasdavid/f8997fb0bdf4dc1c3543 Тим не менш, це все ще не вирішує проблему бажати перевпорядкувати ключі на декількох рівнях JSON об'єкт (перестановка вкладених ключів), ідеї?
Девід Діас

4

Я б сказав, що з концептуальної точки зору було б краще просто залишити старий об’єкт (той, який є у веб-сервісі) таким, який він є, і поставити потрібні вам значення в новий об’єкт. Я припускаю, що ви витягуєте конкретні поля в тій чи іншій точці, якщо не на клієнті, то принаймні на сервері. Те, що ви вирішили використовувати імена полів, які є такими ж, як у веб-сервісу, лише малі регістри, насправді це не змінює. Отже, я б радив зробити щось подібне:

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

Звичайно, я роблю тут всілякі припущення, що може бути неправдою. Якщо це не стосується вас, або якщо це занадто повільно (чи не так? Я не перевіряв, але я думаю, що різниця зменшується, оскільки кількість полів збільшується), будь ласка, проігноруйте все це :)

Якщо ви не хочете цього робити, і вам потрібно підтримувати лише певні веб-переглядачі, ви також можете скористатися новими геттерами, щоб також повернути "верхній регістр (поле)": див. Http://robertnyman.com/2009/05/28 / getters-and-setters-with-javascript-code-sample-sample-demo / та посилання на цій сторінці для отримання додаткової інформації.

Редагувати:

Неймовірно, що це також майже вдвічі швидше, принаймні на моєму FF3.5 на роботі. Дивіться: http://jsperf.com/spiny001


Дуже дякую за ваші зусилля, але випадок використання не маніпулює статичними об'єктами, оскільки їхня структура та глибина не знаходяться. Тож, якщо можливо, я хотів би дотримуватися оригінальної мети питання.
Жан Вінсент

4

Хоча це точно не дає кращого рішення для перейменування ключа, він забезпечує швидкий та простий спосіб ES6 для перейменування всіх ключів в об’єкті, не мутуючи дані, які вони містять.

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

3
Будь ласка, скористайтеся посиланням редагування, щоб пояснити, як працює цей код, а не просто надайте код, оскільки пояснення швидше допоможе майбутнім читачам. Дивіться також Як відповісти . джерело
Джед Фокс

Я згоден. Він не відповідає конкретно на питання, але це допомогло мені виконати роботу як рішення для перейменування всіх ключів в об'єкті. Можливо, я пропустив тему ..
Тюдор Морар

3

Деякі з перелічених на цій сторінці рішень мають деякі побічні ефекти:

  1. впливати на положення ключа в об’єкті, додаючи його внизу (якщо це має значення для вас)
  2. не працюватиме в IE9 + (знову ж, якщо це має значення для вас)

Ось рішення, яке зберігає положення ключа в тому самому місці і сумісне в IE9 +, але має створити новий об'єкт і може бути не найшвидшим рішенням:

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

Зверніть увагу: IE9 може не підтримуватиEach у суворому режимі


2

Ось приклад створення нового об’єкта з перейменованими клавішами.

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

Ще один спосіб із найпотужнішим методом ЗНИЖЕННЯ .

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

Це невелика модифікація, яку я вніс до функції pomber; Щоб мати можливість приймати масив об'єктів замість об'єкта самостійно, а також ви можете активувати індекс. також "Ключі" можуть бути призначені масивом

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

тест

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

На ваш погляд, ваш спосіб оптимізований. Але ви закінчите з упорядкованими ключами. Щойно створений ключ буде доданий в кінці. Я знаю, що ніколи не слід покладатися на замовлення ключів, але якщо вам потрібно зберегти його, вам потрібно буде пройти всі клавіші та сконструювати новий об'єкт по черзі, замінюючи відповідний ключ під час цього процесу.

Подобається це:

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
  • Ви можете скористатися утилітою, щоб впоратися з цим.
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

просто спробуйте в улюбленому редакторі <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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.