JavaScript: объект переименовать ключ

299

Есть ли умный (то есть оптимизированный) способ переименовать ключ в объекте JavaScript?

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

o[ new_key ] = o[ old_key ];
delete o[ old_key ];
Жан Винсент
источник
14
Что вы подразумеваете под "оптимизирован"? Я не думаю, что это может стать более кратким, чем это; нет встроенной операции переименования.
Заостренный
9
Это все, что вы можете получить. Я бы беспокоился о других вещах в моем приложении. И кстати, вы имеете дело с объектами, а не массивами. В JavaScript нет ассоциативных массивов (в строгом смысле).
Феликс Клинг
3
@Джин Винсент: Это так медленно?
Феликс Клинг
8
это самая оптимизированная и базовая версия
Ливингстон Самуэль
4
ваша версия самая быстрая во всех современных браузерах, кроме сафари, пример теста @ jsperf.com/livi-006
Ливингстон Самуэль

Ответы:

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
проголосовал с некоторым скептицизмом по отношению к writing_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;
        }
    }
);
ChaosPandion
источник
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];
nverba
источник
5
Попробуйте: удалить Object.assign (o, {newKey: o.oldKey}). OldKey; Работал хорошо для меня
Тим
2
Почему []вокруг квадратная скобка newKey? Решение работает без этого на самом деле.
Soumya Kanti
2
Хорошо, я получил свой ответ. Это особенность ES6. Для всех, кто сталкивался с таким, как я, вот хороший ответ .
Soumya Kanti
1
Этот ответ был бы сильнее, если бы ваши имена переменных были более многословными.
низкопробный
1
Может быть? Извините, я забыл, что я думал с моим первоначальным комментарием.
низкорамный
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"}
pomber
источник
2
это должен быть принят ответ, отлично работал для моего варианта использования. Мне пришлось преобразовать ответ API, который начинался с префикса Названия стран с ненужной строкой. Превратил Object в массив ключей, отобразил через каждый ключ методом подстроки и возвратил новый объект с новым ключом и значением, проиндексированным по оригинальному ключу
Akin Hwan
1
Это хорошо, так как не удаляет старый ключ. Удаление старого ключа полностью удалит ключ из объекта, если имя ключа не изменилось. В моем примере я конвертировал my_object_propertyв myObjectProperty, однако, если мое свойство было односложным, то ключ был удален. +1
blueprintchris
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"}

ярлык демонстрационного экрана

xgqfrms-gildata
источник
1
Нашел это как более чистое решение, когда вы хотите преобразовать поврежденный объект в совершенно новый реструктурированный.
NJInamdar
5
Хорошо, но я думаю, что const {k1: kA, k2: kB, k3: kC,} = old_obj достаточно? не нужно распространять (если вам не нужна копия old_obj).
Ганс
@ Ганс, да, ты прав. Но в этой ситуации я хочу переименовать некоторые ключи, поэтому я должен распространять их.
xgqfrms-gildata
2
Это требует гораздо больше кода, будет менее эффективным и создает новый объект. Переименование свойства подразумевает изменение объекта. Новее не всегда лучше.
Жан Винсент
16

Если вы не хотите изменять свои данные, рассмотрите эту функцию ...

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

Подробное объяснение Язида Бзадоу https://medium.com/front-end-hacking/immubly-rename-object-keys-in-javascript-5f6353c7b6dd


Mulhoon
источник
Это лучшее решение. Но нужно кое-что понять о спреде оператора.
Франко Фонг
13

Большинство ответов здесь не поддерживают порядок пар ключ-значение JS Object. Например, если на экране имеется форма пар ключ-значение объекта, которую вы хотите изменить, важно сохранить порядок записей объектов.

Способ ES6 циклически проходить через объект 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] }
  }
});

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

afalak
источник
Отлично, это то, что мне было нужно
p0358
12

Вы можете попробовать 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>

Пенни лю
источник
Это решение сработало для меня, но обратите внимание, чтобы всегда возвращать переменную «ключ». Пример: 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, что если это был массив объектов? Нужно ли делать foreach или я тоже могу делать спред?
user3808307
@ 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если ваш объект имеет недопустимую структуру. Работает отлично :)

Novitoll
источник
12
по электронной почте Ой! var object = {b: '123', 'c': 'bbb'}; var str = JSON.stringify (object); 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
если объект содержит функцию или объект даты, это не будет работать
ихсан
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'}
piotr_cz
источник
5

Я бы сделал что-то вроде этого:

function renameKeys(dict, keyMap) {
  return _.reduce(dict, function(newDict, val, oldKey) {
    var newKey = keyMap[oldKey] || oldKey
    newDict[newKey] = val 
    return newDict
  }, {})
}
tldr
источник
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-samples-and-demos / и ссылки на этой странице для получения дополнительной информации.

РЕДАКТИРОВАТЬ:

Невероятно, но это также почти вдвое быстрее, по крайней мере, на моем 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 может не поддерживать forEach в строгом режиме

Джасдип Халса
источник
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

Еще один способ с самым мощным методом REDUCE .

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
источник
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);
Jasp402
источник
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
}
Pablo
источник