JavaScript: chiave di rinomina oggetto


299

Esiste un modo intelligente (ovvero ottimizzato) per rinominare una chiave in un oggetto JavaScript?

Un modo non ottimizzato sarebbe:

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

14
Cosa intendi con "ottimizzato"? Non penso che possa essere più conciso di così; non è presente alcuna operazione incorporata per "rinominare".
Punta il

9
Questo è tutto ciò che puoi ottenere. Mi preoccuperei per altre cose nella mia applicazione. E a proposito, hai a che fare con oggetti, non array. Non ci sono array associativi in ​​JavaScript (in senso stretto).
Felix Kling,

3
@Jean Vincent: è così lento?
Felix Kling,

8
questa è la versione più ottimizzata e di base
Livingston Samuel

4
la tua versione è la più veloce in tutti i browser moderni ad eccezione di Safari, esempio di test case @ jsperf.com/livi-006
Livingston Samuel

Risposte:


193

Il modo più completo (e corretto) per farlo sarebbe, credo:

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

Questo metodo garantisce che la proprietà rinominata si comporti in modo identico a quella originale.

Inoltre, mi sembra che la possibilità di avvolgerlo in una funzione / metodo e metterlo in questione Object.prototypesia irrilevante per quanto riguarda la tua domanda.


1
Questa è una bella soluzione solo ES5 con il vero vantaggio di preservare tutte le proprietà. Quindi questo non è "ottimizzato" ma decisamente più preciso su ES5.
Jean Vincent,

6
votato con un po 'di scetticismo verso writing_things_with_underscores, ma ovviamente ciò era dovuto alla persona che poneva la domanda. questa è la risposta corretta a mio avviso.
Bent Cardan,

3
Nel caso in cui sia importante, credo che questo particolare utilizzo di ES5 sia una soluzione IE9 e più recente.
Scott Stafford,

1
Suggerisco di aggiungere una convalida dell'esistenza di o.old_key. Modifica: if (old_key !== new_key)a: if (old_key !== new_key && o[old_key])
jose

100

È possibile avvolgere il lavoro in una funzione e assegnarlo al Objectprototipo. Forse usa lo stile dell'interfaccia fluente per far fluire più rinominazioni.

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 Specifico

Vorrei che la sintassi non fosse così complessa, ma è sicuramente bello avere più controllo.

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 mi dispiace, ma sono davvero stanco del codice JS infestato da bug, attualmente sto scrivendo una Guida ( github.com/BonsaiDen/JavaScript-Garden ) su tutte le stranezze (inclusa quella che ora hai risolto), questo potrebbe avermi messo in una sorta di modalità rant;) (Rimosso il -1 ora)
Ivo Wetzel,

1
@Ivo - Potresti spiegare perché ritieni che l'estensione del prototipo sia negativa? I prototipi sono stati realizzati per l'estensione del bambino!
ChaosPandion,

5
@Ivo - Sono d'accordo che è rischioso, ma non c'è nulla che mi impedisca di estendere il prototipo se lo sentissi portare a un codice più espressivo. Anche se vengo da una mentalità ECMAScript 5 in cui è possibile contrassegnare una proprietà come non enumerabile tramite Object.defineProperty.
ChaosPandion,

3
@Ivo - Se è pratica corretta usare sempre hasOwnPropertyper verificare la presenza di proprietà e all'interno dei for ... inloop, perché importa se estendiamo Object?
David Tang,

2
Questo codice funziona ma l'avvertenza è che se il nome della nuova chiave esiste già, il suo valore scenderà calpestato: jsfiddle.net/ryurage/B7x8x
Brandon Minton

83

Se stai mutando il tuo oggetto sorgente, ES6 può farlo in una riga.

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

O due righe se si desidera creare un nuovo oggetto.

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

5
Prova: elimina Object.assign (o, {newKey: o.oldKey}). OldKey; Ha funzionato bene per me
Tim

2
Perché c'è il tutore quadrato []intorno newKey? La soluzione funziona senza questo in realtà.
Soumya Kanti,

2
Ok - ho ricevuto la mia risposta. È la funzione ES6. Per chiunque sia incappato in questo come me, ecco una buona risposta .
Soumya Kanti,

1
Questa risposta sarebbe più forte se i nomi delle tue variabili fossero più dettagliati.
lowcrawler,

1
Forse? Scusa, ho dimenticato cosa stavo pensando con il mio commento iniziale.
lowcrawler,

42

Nel caso in cui qualcuno debba rinominare un elenco di proprietà:

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

2
questa dovrebbe essere una risposta accettata, ha funzionato benissimo per il mio caso d'uso. Ho dovuto convertire una risposta API che faceva precedere i nomi dei paesi con una stringa non necessaria. Trasformato Object in array di chiavi e mappato attraverso ciascuna chiave con il metodo di sottostringa e restituito un nuovo oggetto con nuova chiave e valore indicizzato dalla chiave originale
Akin Hwan,

1
Questo è carino in quanto non cancella la vecchia chiave. L'eliminazione della vecchia chiave rimuoverà completamente la chiave dall'oggetto se il nome della chiave non è stato modificato. Nel mio esempio, stavo convertendo my_object_propertyin myObjectProperty, tuttavia, se la mia proprietà era a una sola parola, la chiave è stata rimossa. +1
blueprintchris

25

Vorrei solo usare la ES6(ES2015)strada!

dobbiamo stare al passo con i tempi!

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

collegamento alla schermata demo


1
Trovato questo come soluzione più pulita quando si desidera trasformare un oggetto malformato in un nuovissimo ristrutturato.
NJInamdar,

5
Buono, ma penso che const {k1: kA, k2: kB, k3: kC,} = old_obj sia sufficiente? non è necessario diffondere (a meno che non si desideri una copia di old_obj).
Hans,

@Hans, sì hai ragione. Ma in questa situazione, voglio rinominare alcune chiavi, quindi dovrei diffonderle.
xgqfrms-gildata,

2
Ciò richiede molto più codice, sarà meno efficiente e crea un nuovo oggetto. Rinominare una proprietà implica mutare l'oggetto. Il nuovo non è sempre meglio.
Jean Vincent,


13

La maggior parte delle risposte non riesce a mantenere l'ordine delle coppie chiave-valore di JS Object. Se sullo schermo è presente una forma di coppie chiave-valore oggetto che si desidera modificare, ad esempio, è importante preservare l'ordine delle voci dell'oggetto.

Il modo ES6 di eseguire il looping dell'oggetto JS e di sostituire la coppia chiave-valore con la nuova coppia con un nome chiave modificato sarebbe simile a:

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 soluzione mantiene l'ordine delle voci aggiungendo la nuova voce al posto di quella precedente.


Perfetto, questo è quello di cui avevo bisogno
p0358,

12

Puoi provare 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>


Questa soluzione ha funzionato per me, ma presta attenzione a restituire sempre una variabile "chiave". Esempio: if (key === 'oldKey') {return 'newKey'; } chiave di ritorno;
TomoMiha,

12

Una variante che utilizza l'operatore di distruzione e diffusione dell'oggetto:

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

Forse potresti spiegare cosa sta facendo questo e come funziona?
Jon Adams il

@JeffLowery e se fosse una matrice di oggetti? Devo fare una foreach o posso farlo anche con spread?
user3808307

@ user3808307 È possibile eseguire uno spread su un array e oggetti nell'array.
Jeff Lowery,

7

Personalmente, il modo più efficace per rinominare le chiavi nell'oggetto senza implementare plugin e ruote extra pesanti:

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

object = JSON.parse(str);

Puoi anche avvolgerlo try-catchse il tuo oggetto ha una struttura non valida. Funziona perfettamente :)


12
Ops! var object = {b: '123', 'c': 'bbb'}; var str = JSON.stringify (oggetto); str = str.replace (/ b / g, 'newKey'); str = str.replace (/ c / g, 'newKey2'); object = JSON.parse (str);
BlondinkaBrain,

4
Come si confronta questo per la velocità?
mix3d

16
Incontra anche un potenziale problema da qualche parte nei valori dei dati è la stessa stringa del vecchio nome della chiave.
mix3d

4
"Funziona perfettamente" per alcune definizioni di "perfettamente". Provare per rinominare 'a'in { a: 1, aa: 2, aaa: 3}o provare per rinominare un oggetto con valori che sono qualcosa di più di stringhe o numeri o booleani, o provare con riferimenti circolari.
rsp,

1
se l'oggetto contiene una funzione o un oggetto data, questo non funzionerà
ikhsan,

7

Per aggiungere il prefisso a ciascuna chiave:

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

Farei qualcosa del genere:

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

3
Creata una versione aggiornata della tua soluzione che non trasforma le chiavi statiche in "non definite" qui: gist.github.com/diasdavid/f8997fb0bdf4dc1c3543 Tuttavia, non risolve ancora il problema di voler rimappare le chiavi a più livelli di JSON oggetto (rimappatura delle chiavi nidificate), idee?
David Dias,

4

Direi che sarebbe meglio da un punto di vista concettuale lasciare semplicemente il vecchio oggetto (quello del servizio web) così com'è e inserire i valori necessari in un nuovo oggetto. Presumo che tu stia estraendo campi specifici in un punto o nell'altro comunque, se non sul client, almeno sul server. Il fatto che tu abbia scelto di utilizzare nomi di campo uguali a quelli del servizio Web, solo in minuscolo, non cambia questo. Quindi, consiglierei di fare qualcosa del genere:

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

Certo, sto facendo tutti i tipi di ipotesi qui, il che potrebbe non essere vero. Se questo non si applica a te, o se è troppo lento (vero? Non ho ancora testato, ma immagino che la differenza diminuisca all'aumentare del numero di campi), per favore ignora tutto questo :)

Se non vuoi farlo e devi solo supportare browser specifici, puoi anche usare i nuovi getter per restituire anche "maiuscolo (campo)": vedi http://robertnyman.com/2009/05/28 / getters-and-setters-with-javascript-code-samples-and-demos / e i collegamenti su quella pagina per ulteriori informazioni.

MODIFICARE:

Incredibilmente, anche questo è quasi il doppio più veloce, almeno sulla mia FF3.5 al lavoro. Vedi: http://jsperf.com/spiny001


Grazie mille per i tuoi sforzi, ma il caso d'uso non manipola oggetti statici poiché la loro struttura e profondità non sono conosciute. Quindi, se possibile, vorrei attenermi allo scopo originale della domanda.
Jean Vincent,

4

Sebbene ciò non fornisca esattamente una soluzione migliore per rinominare una chiave, fornisce un modo ES6 rapido e semplice per rinominare tutte le chiavi in ​​un oggetto senza mutare i dati che contengono.

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

3
Utilizza il link di modifica per spiegare come funziona questo codice e non limitarti a fornire il codice, poiché è più probabile che una spiegazione aiuti i lettori futuri. Vedi anche Come rispondere . fonte
Jed Fox,

Sono d'accordo. Non risponde alla domanda in modo specifico ma mi ha aiutato a svolgere il lavoro come soluzione per rinominare tutte le chiavi dell'oggetto. Forse ho perso l'argomento ..
Tudor Morar,

3

Alcune delle soluzioni elencate in questa pagina hanno alcuni effetti collaterali:

  1. influisce sulla posizione della chiave nell'oggetto, aggiungendola in fondo (se questo è importante per te)
  2. non funzionerebbe in IE9 + (di nuovo, se questo è importante per te)

Ecco una soluzione che mantiene la posizione della chiave nello stesso posto ed è compatibile in IE9 +, ma deve creare un nuovo oggetto e potrebbe non essere la soluzione più veloce:

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

Nota: IE9 potrebbe non supportare Ognuno in modalità rigorosa


2

Ecco un esempio per creare un nuovo oggetto con chiavi rinominate.

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

Ancora un altro modo con il metodo REDUCE più potente .

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

Questa è una piccola modifica che ho apportato alla funzione di pomber; Per essere in grado di prendere una matrice di oggetti anziché un solo oggetto e puoi anche attivare l'indice. anche i "tasti" possono essere assegnati da un 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;
}

test

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

La tua strada è ottimizzata, secondo me. Ma finirai con le chiavi riordinate. La chiave appena creata verrà aggiunta alla fine. So che non dovresti mai fare affidamento sull'ordine delle chiavi, ma se devi preservarlo, dovrai esaminare tutte le chiavi e costruire un nuovo oggetto uno per uno, sostituendo la chiave in questione durante quel processo.

Come questo:

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
  • È possibile utilizzare un'utilità per gestire questo.
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

provalo nel tuo editor preferito <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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.