JavaScript: Chave de Renomeação de Objeto

299

Existe uma maneira inteligente (ou seja, otimizada) de renomear uma chave em um objeto javascript?

Uma maneira não otimizada seria:

o[ new_key ] = o[ old_key ];
delete o[ old_key ];
Jean Vincent
fonte
14
O que você quer dizer com "otimizado"? Não acho que seja mais conciso que isso; não há operação interna "renomear".
Pointy
9
Isso é tudo que você pode obter. Eu me preocuparia com outras coisas no meu aplicativo. E btw, você está lidando com objetos, não matrizes. Não há matrizes associativas em JavaScript (em sentido estrito).
Felix Kling
3
@ Jean Vincent: É tão lento?
Felix Kling
8
este é o mais otimizado e versão básica
Livingston Samuel
4
sua versão é o mais rápido em todos os navegadores modernos, exceto safari, caso de teste da amostra @ jsperf.com/livi-006
Livingston Samuel

Respostas:

193

A maneira mais completa (e correta) de fazer isso seria, acredito:

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

Este método garante que a propriedade renomeada se comporte de forma idêntica à original.

Além disso, parece-me que a possibilidade de agrupar isso em uma função / método e colocá-lo Object.prototypeé irrelevante em relação à sua pergunta.

Valeriu Paloş
fonte
1
Esta é uma boa solução somente para ES5, com a vantagem real de preservar todas as propriedades. Portanto, isso não é "otimizado", mas definitivamente mais preciso no ES5.
10243 Jean Vincent
6
votado com algum ceticismo em relação a escrever_coisas_com_escores, mas é claro que isso se deveu à pessoa que fez a pergunta. esta é a resposta correta na minha opinião.
Bent Cardan
3
Caso isso aconteça, acredito que esse uso específico do ES5 seja uma solução para o IE9 e mais recente.
22314 Scott Stafford
1
Sugiro adicionar uma validação de que o.old_key existe. Alterando: if (old_key !== new_key)para: if (old_key !== new_key && o[old_key])
jose
100

Você pode agrupar o trabalho em uma função e atribuí-lo ao Objectprotótipo. Talvez use o estilo de interface fluente para fazer com que várias renomeações fluam.

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

Específico do ECMAScript 5

Eu gostaria que a sintaxe não fosse tão complexa, mas é definitivamente bom ter mais controle.

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
fonte
4
@ChaosPandion desculpe-me por isso, mas estou muito cansado do código JS repleto de bugs, atualmente estou escrevendo um Guia ( github.com/BonsaiDen/JavaScript-Garden ) sobre todas as peculiaridades (incluindo a que você corrigiu agora), isso pode ter me colocar em algum tipo de modo de discurso;) (Removido o -1 agora)
Ivo Wetzel
1
@ Ivo - Você poderia explicar por que sente que estender o protótipo é ruim? Protótipos foram feitos para estender o bebê!
precisa saber é o seguinte
5
@Ivo - Concordo que é arriscado, mas não há nada que me impeça de estender o protótipo, se eu achar que isso leva a um código mais expressivo. Embora eu seja proveniente de uma mentalidade do ECMAScript 5, é possível marcar uma propriedade como não enumerável via Object.defineProperty.
precisa saber é o seguinte
3
@ Ivo - Se é uma prática adequada sempre usar hasOwnPropertypara verificar propriedades e dentro de for ... inloops, por que importa se estendemos o Object?
David Tang
2
Esse código funciona, mas a ressalva é que, se o novo nome de chave já existe, o seu valor vai ficar para baixo trilhado: jsfiddle.net/ryurage/B7x8x
Brandon Minton
83

Se você estiver modificando seu objeto de origem, o ES6 poderá fazê-lo em uma linha.

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

Ou duas linhas, se você deseja criar um novo objeto.

const newObject = {};
delete Object.assign(newObject, o, {[newKey]: o[oldKey] })[oldKey];
Nverba
fonte
5
Tente: excluir Object.assign (o, {newKey: o.oldKey}). OldKey; Funcionou bem para mim
Tim
2
Por que existe a chave quadrado []em torno de newKey? A solução funciona sem isso, na verdade.
Soumya Kanti
2
Ok - eu recebi minha resposta. É o recurso ES6. Para quem mais se deparou com isso como eu, aqui está uma boa resposta .
Soumya Kanti
1
Essa resposta seria mais forte se os nomes das variáveis ​​fossem mais detalhados.
Lowcrawler
1
Possivelmente? Desculpe, esqueci o que estava pensando com meu comentário inicial.
Lowcrawler 9/08/19
42

Caso alguém precise renomear uma lista de propriedades:

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

Uso:

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
fonte
2
essa resposta deve ser aceita, funcionou muito bem para o meu caso de uso. Eu tive que converter uma resposta da API que prefixava os nomes de países com uma seqüência desnecessária. Transformaram um objecto na matriz de teclas e mapeadas através de cada tecla com o método subcadeia e devolvido um novo objecto com nova chave e o valor indexado por chave original
Akin Hwan
1
Isso é bom, pois não exclui a chave antiga. Excluir a chave antiga removerá a chave completamente do objeto se o nome da chave não tiver sido alterado. No meu exemplo, eu estava convertendo my_object_propertypara myObjectProperty, no entanto, se minha propriedade tivesse uma palavra, a chave foi removida. 1
blueprintchris
25

Gostaria apenas de usar o ES6(ES2015)caminho!

precisamos acompanhar os tempos!

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

atalho na tela de demonstração

xgqfrms-gildata
fonte
1
Encontrei essa como uma solução mais limpa quando você deseja transformar um objeto malformado em um novo e reestruturado.
NJInamdar
5
Bom, mas acho que const {k1: kA, k2: kB, k3: kC,} = old_obj é suficiente? não é necessário se espalhar (a menos que você queira uma cópia do old_obj).
24718 Hans
@ Hans, sim, você está certo. Mas nessa situação, quero renomear algumas chaves, portanto, devo espalhá-las.
Xgqfrms-gildata
2
Isso requer muito mais código, será menos eficiente e cria um novo objeto. Renomear uma propriedade implica alterar o objeto. Mais recente nem sempre é melhor.
Jean Vincent
13

A maioria das respostas aqui falham em manter a ordem dos pares de valores-chave do JS Object. Se você possui uma forma de pares de valores-chave de objeto na tela que deseja modificar, por exemplo, é importante preservar a ordem das entradas do objeto.

A maneira do ES6 de percorrer o objeto JS e substituir o par de valores-chave pelo novo par por um nome de chave modificado seria algo como:

let newWordsObject = {};

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

A solução preserva a ordem das entradas adicionando a nova entrada no lugar da antiga.

afalak
fonte
Perfeito, isso é o que eu precisava
p0358
12

Você pode tentar 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
fonte
Essa solução funcionou para mim, mas preste atenção para sempre retornar uma variável 'key'. Exemplo: if (key === 'oldKey') {return 'newKey'; } chave de retorno;
TomoMiha 2/10/19
12

Uma variação usando o operador de desestruturação e propagação de objetos:

    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
fonte
Talvez você possa explicar o que isso está fazendo e como funciona?
John Adams
@JeffLowery e se fosse uma matriz de objetos? Preciso fazer um foreach ou também posso espalhar?
user3808307
@ user3808307 Você pode fazer uma propagação em uma matriz e objetos na matriz.
Jeff Lowery
7

Pessoalmente, a maneira mais eficaz de renomear chaves no objeto sem implementar plugins e rodas extra pesados:

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

object = JSON.parse(str);

Você também pode envolvê-lo try-catchse seu objeto tiver uma estrutura inválida. Funciona perfeitamente :)

Novitoll
fonte
12
ooops! var objeto = {b: '123', 'c': 'bbb'}; var str = JSON.stringify (objeto); str = str.replace (/ b / g, 'newKey'); str = str.replace (/ c / g, 'newKey2'); objeto = JSON.parse (str);
BlondinkaBrain 16/02
4
Como isso se compara à velocidade?
mix3d #
16
Também ocorre um problema em potencial em algum lugar nos valores de dados, a mesma sequência que o nome da chave antiga.
mix3d
4
"Funciona perfeitamente" para certas definições de "perfeitamente". Tente mudar o nome 'a'em { a: 1, aa: 2, aaa: 3}ou tentar mudar o nome de um objeto com valores que são nada mais do que cordas ou números ou booleans, ou tentar com referências circulares.
Rsp #
1
se o objeto contém função ou data de objeto, isso não vai funcionar
Ikhsan
7

Para adicionar prefixo a cada chave:

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
fonte
5

Eu faria algo assim:

function renameKeys(dict, keyMap) {
  return _.reduce(dict, function(newDict, val, oldKey) {
    var newKey = keyMap[oldKey] || oldKey
    newDict[newKey] = val 
    return newDict
  }, {})
}
tldr
fonte
3
Fiz uma versão atualizada da sua solução que não transforma chaves estáticas em 'indefinidas' aqui: gist.github.com/diasdavid/f8997fb0bdf4dc1c3543 No entanto, ainda não existe o problema de querer remapear as chaves em vários níveis do JSON objeto (refazendo chaves aninhadas), ideias?
David Dias
4

Eu diria que seria melhor, do ponto de vista conceitual, deixar o objeto antigo (o do serviço da web) como está e colocar os valores necessários em um novo objeto. Suponho que você esteja extraindo campos específicos em um ponto ou outro de qualquer maneira, se não no cliente, pelo menos no servidor. O fato de você optar por usar nomes de campos iguais aos do serviço da Web, apenas em minúsculas, não altera isso. Então, eu aconselho a fazer algo assim:

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

Claro, estou fazendo todos os tipos de suposições aqui, o que pode não ser verdade. Se isso não se aplicar a você, ou se for muito lento (não é? Não testei, mas imagino que a diferença diminua à medida que o número de campos aumenta), ignore tudo isso :)

Se você não quiser fazer isso e precisar suportar apenas navegadores específicos, também poderá usar os novos getters para também retornar "maiúsculas (campo)": consulte http://robertnyman.com/2009/05/28 / getters-and-setters-with-javascript-code-samples-and-demos / e os links nessa página para obter mais informações.

EDITAR:

Incrivelmente, isso também é quase duas vezes mais rápido, pelo menos no meu FF3.5 no trabalho. Veja: http://jsperf.com/spiny001

Normando Espinhoso
fonte
Muito obrigado por seus esforços, mas o caso de uso não manipula objetos estáticos, pois sua estrutura e profundidade são desconhecidas. Portanto, se possível, gostaria de manter o escopo original da pergunta.
Jean Vincent
4

Embora isso não forneça exatamente uma solução melhor para renomear uma chave, ele fornece uma maneira rápida e fácil do ES6 de renomear todas as chaves em um objeto sem alterar os dados que eles contêm.

let b = {a: ["1"], b:["2"]};
Object.keys(b).map(id => {
  b[`root_${id}`] = [...b[id]];
  delete b[id];
});
console.log(b);
Tudor Morar
fonte
3
Use o link editar para explicar como esse código funciona e não apenas forneça o código, pois é mais provável que uma explicação ajude futuros leitores. Veja também Como responder . fonte
Jed Fox 27/01
Concordo. Ele não responde à pergunta especificamente, mas me ajudou a concluir o trabalho como uma solução para renomear todas as chaves no objeto. Talvez eu perdi o assunto ..
Tudor Morar
3

Algumas das soluções listadas nesta página têm alguns efeitos colaterais:

  1. afeta a posição da chave no objeto, adicionando-a ao final (se isso for importante para você)
  2. não funcionaria no IE9 + (novamente, se isso for importante para você)

Aqui está uma solução que mantém a posição da chave no mesmo local e é compatível no IE9 +, mas precisa criar um novo objeto e pode não ser a solução mais rápida:

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

Observe: O IE9 pode não suportar forEach no modo estrito

Jasdeep Khalsa
fonte
2

Aqui está um exemplo para criar um novo objeto com chaves renomeadas.

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

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

Mais uma maneira com o mais poderoso método 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
fonte
1

Essa é uma pequena modificação que fiz na função de pomber; Para poder pegar uma matriz de objetos em vez de um objeto sozinho e você também pode ativar o índice. também as "Teclas" podem ser atribuídas por uma matriz

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

teste

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
fonte
1

Seu caminho é otimizado, na minha opinião. Mas você terminará com chaves reordenadas. A chave recém-criada será anexada no final. Eu sei que você nunca deve confiar na ordem das chaves, mas se precisar preservá-las, precisará passar por todas as chaves e construir um novo objeto, um por um, substituindo a chave em questão durante esse processo.

Como isso:

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
fonte
0
  • Você pode usar um utilitário para lidar com isso.
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
fonte
0

tente no seu editor favorito <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
fonte