JavaScript: clave de cambio de nombre de objeto

299

¿Hay alguna forma inteligente (es decir, optimizada) de cambiar el nombre de una clave en un objeto javascript?

Una forma no optimizada sería:

o[ new_key ] = o[ old_key ];
delete o[ old_key ];
Jean Vincent
fuente
14
¿Qué quieres decir con "optimizado"? No creo que pueda ser más conciso que eso; no hay operación incorporada de "cambio de nombre".
Puntiagudo
99
Eso es todo lo que puedes conseguir. Me preocuparía por otras cosas en mi aplicación. Y por cierto, se trata de objetos, no de matrices. No hay matrices asociativas en JavaScript (en sentido estricto).
Felix Kling
3
@Jean Vincent: ¿Es tan lento?
Felix Kling
8
esta es la versión más optimizada y básica
Livingston Samuel
44
su versión es la más rápida en todos los navegadores modernos, excepto safari, caso de prueba de muestra @ jsperf.com/livi-006
Livingston Samuel

Respuestas:

193

Creo que la forma más completa (y correcta) de hacerlo sería:

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

Este método asegura que la propiedad renombrada se comporte de manera idéntica a la original.

Además, me parece que la posibilidad de envolver esto en una función / método y ponerlo Object.prototypees irrelevante con respecto a su pregunta.

Valeriu Paloş
fuente
1
Esta es una buena solución solo para ES5 con la ventaja real de preservar todas las propiedades. Por lo tanto, esto no está "optimizado", pero definitivamente es más preciso en ES5.
Jean Vincent
66
votó con cierto escepticismo hacia escribir cosas_con_colecciones, pero por supuesto eso se debió a la persona que hizo la pregunta. Esta es la respuesta correcta en mi opinión.
Doblado Cardan
3
En caso de que sea importante, creo que este uso particular de ES5 es una solución IE9 y más nueva.
Scott Stafford
1
Sugiero agregar una validación de que o.old_key existe. Cambio: if (old_key !== new_key)a: if (old_key !== new_key && o[old_key])
jose
100

Puede ajustar el trabajo en una función y asignarlo al Objectprototipo. Tal vez use el estilo de interfaz fluido para hacer fluir múltiples cambios de nombre.

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 específico

Desearía que la sintaxis no fuera tan compleja, pero definitivamente es bueno tener más control.

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
fuente
44
@ChaosPandion lo siento, pero estoy realmente cansado del código JS con errores, actualmente estoy escribiendo una Guía ( github.com/BonsaiDen/JavaScript-Garden ) sobre todas las peculiaridades (incluida la que ahora ha solucionado), esto podría haberme puesto en algún tipo de modo despotricar;) (Eliminado el -1 ahora)
Ivo Wetzel
1
@Ivo - ¿Podría explicar por qué siente que extender el prototipo es malo? ¡Se hicieron prototipos para extender al bebé!
ChaosPandion
55
@Ivo: estoy de acuerdo en que es arriesgado, pero no hay nada que me impida extender el prototipo si siento que conduce a un código más expresivo. Aunque vengo de una mentalidad ECMAScript 5 donde puede marcar una propiedad como no enumerable a través de Object.defineProperty.
ChaosPandion
3
@Ivo: si es una práctica adecuada usar siempre hasOwnPropertypara verificar las propiedades y dentro de los for ... inbucles, entonces ¿por qué importa si ampliamos Object?
David Tang el
2
Este código funciona, pero la advertencia es que si el nuevo nombre de la clave ya existe, su valor se reducirá: jsfiddle.net/ryurage/B7x8x
Brandon Minton
83

Si está mutando su objeto fuente, ES6 puede hacerlo en una línea.

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

O dos líneas si desea crear un nuevo objeto.

const newObject = {};
delete Object.assign(newObject, o, {[newKey]: o[oldKey] })[oldKey];
nverba
fuente
55
Intente: eliminar Object.assign (o, {newKey: o.oldKey}). OldKey; Me funcionó bien
Tim
2
¿Por qué hay una llave cuadrada []alrededor newKey? La solución funciona sin eso en realidad.
Soumya Kanti
2
Ok, recibí mi respuesta. Es la función ES6. Para cualquiera que haya tropezado con esto como yo, aquí hay una buena respuesta .
Soumya Kanti
1
Esta respuesta sería más fuerte si los nombres de sus variables fueran más detallados.
lowcrawler
1
¿Quizás? Lo siento, olvido lo que estaba pensando con mi comentario inicial.
lowcrawler
42

En caso de que alguien necesite cambiar el nombre de una lista de propiedades:

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
fuente
2
esta respuesta debe ser aceptada, funcionó muy bien para mi caso de uso. Tuve que convertir una respuesta de API que tenía como prefijo los nombres de países con una cadena innecesaria. Convirtió el objeto en una matriz de claves y lo mapeó a través de cada clave con el método de subcadena y devolvió un nuevo objeto con una clave y un valor nuevos indexados por clave original
Akin Hwan
1
Esto es bueno ya que no elimina la clave anterior. Si elimina la clave anterior, la eliminará por completo del objeto si el nombre de la clave no ha cambiado. En mi ejemplo, me estaba convirtiendo my_object_propertya myObjectProperty, sin embargo, si mi propiedad era de una sola palabra, entonces la clave fue eliminada. +1
blueprintchris
25

¡Me gustaría simplemente usar el ES6(ES2015)camino!

¡Necesitamos mantenernos al día!

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

acceso directo a la pantalla de demostración

xgqfrms-gildata
fuente
1
Encontré esta como una solución más limpia cuando desea transformar un objeto malformado en uno nuevo reestructurado.
NJInamdar
55
Buena, pero creo que const {k1: kA, k2: kB, k3: kC,} = old_obj es suficiente? no es necesario difundir (a menos que desee una copia de old_obj).
Hans
@ Hans, sí, tienes razón. Pero en esta situación, quiero cambiar el nombre de algunas claves, por lo que debería difundirlas.
xgqfrms-gildata
2
Esto requiere mucho más código, será menos eficiente y creará un nuevo objeto. Renombrar una propiedad implica mutar el objeto. Más nuevo no siempre es mejor.
Jean Vincent
13

La mayoría de las respuestas aquí no logran mantener el orden de pares clave-valor del objeto JS. Si tiene una forma de pares de clave-valor de objeto en la pantalla que desea modificar, por ejemplo, es importante preservar el orden de las entradas de objeto.

La forma ES6 de recorrer el objeto JS y reemplazar el par clave-valor con el nuevo par con un nombre clave modificado sería algo así 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] }
  }
});

La solución conserva el orden de las entradas al agregar la nueva entrada en lugar de la anterior.

afalak
fuente
Perfecto, esto es lo que necesitaba
p0358
12

Puedes probar 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
fuente
Esta solución funcionó para mí, pero preste atención a devolver siempre una variable 'clave'. Ejemplo: if (key === 'oldKey') {return 'newKey'; } tecla de retorno;
TomoMiha
12

Una variación usando el operador de desestructuración y propagación 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
fuente
¿Quizás podría explicar qué está haciendo esto y cómo funciona?
Jon Adams
@JeffLowery, ¿y si se tratara de una matriz de objetos? ¿Tengo que hacer un foreach o también puedo hacerlo con spread?
user3808307
@ user3808307 Puede hacer una propagación en una matriz y objetos en la matriz.
Jeff Lowery
7

Personalmente, la forma más efectiva de cambiar el nombre de las claves en el objeto sin implementar complementos y ruedas extra pesados:

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

object = JSON.parse(str);

También puede envolverlo try-catchsi su objeto tiene una estructura no válida. Funciona perfectamente :)

Novitoll
fuente
12
ooops! objeto var = {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
44
¿Cómo se compara esto con la velocidad?
mix3d
16
También se encuentra con un posible problema de que en algún lugar de los valores de datos se encuentra la misma cadena que el nombre de la clave anterior.
mix3d
44
"Funciona perfectamente" para ciertas definiciones de "perfectamente". Trate de cambiar el nombre 'a'de { a: 1, aa: 2, aaa: 3}o tratar de cambiar el nombre de un objeto con valores que son algo más que cadenas o números o booleanos, o tratar con referencias circulares.
rsp
1
si el objeto contiene una función o un objeto de fecha, esto no funcionará
ikhsan
7

Para agregar prefijo a cada clave:

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

Haría algo como esto:

function renameKeys(dict, keyMap) {
  return _.reduce(dict, function(newDict, val, oldKey) {
    var newKey = keyMap[oldKey] || oldKey
    newDict[newKey] = val 
    return newDict
  }, {})
}
tldr
fuente
3
Hice una versión actualizada de su solución que no transforma las claves estáticas en 'indefinidas' aquí: gist.github.com/diasdavid/f8997fb0bdf4dc1c3543 Sin embargo, aún no supera el problema de querer reasignar claves en varios niveles de JSON objeto (remapeando claves anidadas), ideas?
David Dias
4

Diría que sería mejor desde un punto de vista conceptual simplemente dejar el objeto antiguo (el del servicio web) como está, y poner los valores que necesita en un objeto nuevo. Supongo que está extrayendo campos específicos en un punto u otro de todos modos, si no en el cliente, al menos en el servidor. El hecho de que elija usar nombres de campo que sean los mismos que los del servicio web, solo en minúsculas, realmente no cambia esto. Entonces, aconsejaría hacer algo como esto:

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

Por supuesto, estoy haciendo todo tipo de suposiciones aquí, lo que puede no ser cierto. Si esto no se aplica a usted, o si es demasiado lento (¿no es así? No lo he probado, pero imagino que la diferencia se reduce a medida que aumenta el número de campos), ignore todo esto :)

Si no desea hacer esto, y solo tiene que admitir navegadores específicos, también puede usar los nuevos captadores para devolver también "mayúsculas (campo)": consulte http://robertnyman.com/2009/05/28 / getters-and-setters-with-javascript-code-samples-and-demos / y los enlaces en esa página para obtener más información.

EDITAR:

Increíblemente, esto también es casi el doble de rápido, al menos en mi FF3.5 en el trabajo. Ver: http://jsperf.com/spiny001

Espinoso normando
fuente
Muchas gracias por sus esfuerzos, pero el caso de uso no manipula objetos estáticos ya que su estructura y profundidad son desconocidas. Entonces, si es posible, me gustaría mantener el alcance original de la pregunta.
Jean Vincent
4

Si bien esto no proporciona exactamente una mejor solución para cambiar el nombre de una clave, proporciona una forma ES6 rápida y fácil de cambiar el nombre de todas las claves en un objeto sin mutar los datos que contienen.

let b = {a: ["1"], b:["2"]};
Object.keys(b).map(id => {
  b[`root_${id}`] = [...b[id]];
  delete b[id];
});
console.log(b);
Tudor Morar
fuente
3
Utilice el enlace de edición para explicar cómo funciona este código y no solo dé el código, ya que es más probable que una explicación ayude a los futuros lectores. Ver también Cómo responder . fuente
Jed Fox
Estoy de acuerdo. No responde la pregunta específicamente, pero me ayudó a hacer el trabajo como una solución para renombrar todas las claves del objeto. Tal vez me perdí el tema ...
Tudor Morar
3

Algunas de las soluciones enumeradas en esta página tienen algunos efectos secundarios:

  1. afectará la posición de la clave en el objeto, agregándola al final (si esto le importa)
  2. no funcionaría en IE9 + (de nuevo, si esto te importa)

Aquí hay una solución que mantiene la posición de la clave en el mismo lugar y es compatible con IE9 +, pero tiene que crear un nuevo objeto y puede no ser la solución más 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;
}

Tenga en cuenta: IE9 puede no ser compatible con cada uno en modo estricto

Jasdeep Khalsa
fuente
2

Aquí hay un ejemplo para crear un nuevo objeto con claves renombradas.

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

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

Otra forma más con el método REDUCE más poderoso .

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

Esta es una pequeña modificación que hice a la función de pomber; Para poder tomar una matriz de objetos en lugar de un objeto solo y también puede activar el índice. También las "claves" pueden ser asignadas por una 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;
}

prueba

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

Tu camino está optimizado, en mi opinión. Pero terminarás con las claves reordenadas. La clave recién creada se agregará al final. Sé que nunca debe confiar en el orden de las claves, pero si necesita preservarlo, deberá revisar todas las claves y construir un nuevo objeto uno por uno, reemplazando la clave en cuestión durante ese proceso.

Me gusta esto:

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;
Tomás M
fuente
0
  • Puede usar una utilidad para manejar esto.
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
fuente
0

solo pruébalo en tu 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
fuente