JavaScript: clé de changement de nom d'objet

299

Existe-t-il un moyen intelligent (c'est-à-dire optimisé) de renommer une clé dans un objet javascript?

Une manière non optimisée serait:

o[ new_key ] = o[ old_key ];
delete o[ old_key ];
Jean Vincent
la source
14
Qu'entendez-vous par «optimisé»? Je ne pense pas que cela puisse devenir plus concis que cela; il n'y a pas d'opération intégrée "renommer".
Pointy
9
C'est tout ce que vous pouvez obtenir. Je m'inquiéterais d'autres choses dans ma candidature. Et btw, vous avez affaire à des objets, pas à des tableaux. Il n'y a pas de tableaux associatifs en JavaScript (au sens strict).
Felix Kling
3
@Jean Vincent: Est-ce si lent?
Felix Kling
8
c'est la version la plus optimisée et la plus basique
Livingston Samuel
4
votre version est la plus rapide de tous les navigateurs modernes sauf safari, exemple de cas de test @ jsperf.com/livi-006
Livingston Samuel

Réponses:

193

La façon la plus complète (et correcte) de procéder serait, je crois:

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

Cette méthode garantit que la propriété renommée se comporte de manière identique à la propriété d'origine.

De plus, il me semble que la possibilité d'envelopper cela dans une fonction / méthode et de l'intégrer Object.prototypen'est pas pertinente en ce qui concerne votre question.

Valeriu Paloş
la source
1
Il s'agit d'une belle solution uniquement ES5 avec le vrai plus de préserver toutes les propriétés. Ce n'est donc pas "optimisé" mais définitivement plus précis sur ES5.
Jean Vincent
6
a voté avec un certain scepticisme à l'égard de writing_things_with_underscores, mais bien sûr, cela était dû à la personne qui posait la question. c'est la bonne réponse à mon avis.
Bent Cardan
3
Au cas où cela importerait, je pense que cette utilisation particulière d'ES5 est une solution IE9 et plus récente.
Scott Stafford du
1
Je suggère d'ajouter une validation de l'existence de o.old_key. Changement: if (old_key !== new_key)vers: if (old_key !== new_key && o[old_key])
jose
100

Vous pouvez envelopper le travail dans une fonction et l'affecter au Objectprototype. Peut-être utiliser le style d'interface fluide pour faire couler plusieurs renommages.

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

Spécifique à ECMAScript 5

Je souhaite que la syntaxe ne soit pas aussi complexe mais c'est vraiment bien d'avoir plus de contrôle.

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
la source
4
@ChaosPandion désolé, mais je suis vraiment fatigué du code JS bug bug, j'écris actuellement un guide ( github.com/BonsaiDen/JavaScript-Garden ) sur toutes les bizarreries (y compris celle que vous avez maintenant corrigé), cela pourrait m'avoir mis dans une sorte de mode délire;) (Suppression du -1 maintenant)
Ivo Wetzel
1
@Ivo - Pourriez-vous expliquer pourquoi vous pensez que l'extension du prototype est mauvaise? Des prototypes ont été faits pour étendre bébé!
ChaosPandion
5
@Ivo - Je reconnais que c'est risqué, mais rien ne m'empêchera d'étendre le prototype si je pensais que cela conduirait à un code plus expressif. Bien que je vienne d'un état d'esprit ECMAScript 5 où vous pouvez marquer une propriété comme non énumérable via Object.defineProperty.
ChaosPandion
3
@Ivo - S'il est recommandé de toujours utiliser hasOwnPropertypour vérifier les propriétés et les for ... inboucles, alors pourquoi est-ce important si nous étendons Object?
David Tang
2
Ce code fonctionne mais la mise en garde est que si le nouveau nom de clé existe déjà, sa valeur va baisser: jsfiddle.net/ryurage/B7x8x
Brandon Minton
83

Si vous mutez votre objet source, ES6 peut le faire sur une seule ligne.

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

Ou deux lignes si vous souhaitez créer un nouvel objet.

const newObject = {};
delete Object.assign(newObject, o, {[newKey]: o[oldKey] })[oldKey];
nverba
la source
5
Essayez: supprimez Object.assign (o, {newKey: o.oldKey}). OldKey; A bien fonctionné pour moi
Tim
2
Pourquoi y a-t-il une accolade carrée []autour newKey? La solution fonctionne sans cela en fait.
Soumya Kanti
2
Ok - j'ai eu ma réponse. C'est la fonction ES6. Pour quelqu'un d'autre qui est tombé dessus comme moi, voici une bonne réponse .
Soumya Kanti
1
Cette réponse serait plus forte si vos noms de variables étaient plus verbeux.
lowcrawler
1
Peut-être? Désolé, j'oublie ce que je pensais avec mon premier commentaire.
lowcrawler
42

Si quelqu'un doit renommer une liste de propriétés:

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

Usage:

const obj = { a: "1", b: "2" };
const newKeys = { a: "A", c: "C" };
const renamedObj = renameKeys(obj, newKeys);
console.log(renamedObj);
// {A:"1", b:"2"}
bombardier
la source
2
cette réponse devrait être acceptée, a très bien fonctionné pour mon cas d'utilisation. J'ai dû convertir une réponse API qui préfixait les noms de pays avec une chaîne inutile. A transformé l'objet en tableau de clés et mappé à travers chaque clé avec la méthode de sous-chaîne et a renvoyé un nouvel objet avec une nouvelle clé et une valeur indexée par la clé d'origine
Akin Hwan
1
C'est bien car cela ne supprime pas l'ancienne clé. La suppression de l'ancienne clé supprimera entièrement la clé de l'objet si le nom de la clé n'a pas changé. Dans mon exemple, je convertissais my_object_propertyen myObjectProperty, cependant, si ma propriété était composée d'un seul mot, la clé a été supprimée. +1
blueprintchris
25

Je voudrais juste utiliser le ES6(ES2015)chemin!

nous devons suivre le temps!

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

raccourci de l'écran de démonstration

xgqfrms-gildata
la source
1
J'ai trouvé celle-ci comme une solution plus propre lorsque vous souhaitez transformer un objet mal formé en un nouvel objet restructuré.
NJInamdar
5
Bon, mais je pense que const {k1: kA, k2: kB, k3: kC,} = old_obj est suffisant? pas besoin de se propager (sauf si vous voulez une copie de old_obj).
Hans
@Hans, ouais tu as raison. Mais dans cette situation, je veux renommer certaines clés, je dois donc les répartir.
xgqfrms-gildata
2
Cela nécessite beaucoup plus de code, sera moins efficace et crée un nouvel objet. Renommer une propriété implique de muter l'objet. Plus récent n'est pas toujours meilleur.
Jean Vincent
13

La plupart des réponses ici ne parviennent pas à maintenir l'ordre des paires clé-valeur de l'objet JS. Si vous avez une forme de paires clé-valeur d'objet à l'écran que vous souhaitez modifier, par exemple, il est important de conserver l'ordre des entrées d'objet.

La façon ES6 de parcourir l'objet JS et de remplacer la paire clé-valeur par la nouvelle paire avec un nom de clé modifié serait quelque chose comme:

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 solution préserve l'ordre des entrées en ajoutant la nouvelle entrée à la place de l'ancienne.

afalak
la source
Parfait, c'est ce dont j'avais besoin
p0358
12

Vous pouvez essayer le 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
la source
Cette solution a fonctionné pour moi, mais faites attention à toujours renvoyer une variable «clé». Exemple: if (key === 'oldKey') {return 'newKey'; } touche retour;
TomoMiha
12

Une variation utilisant l'opérateur de déstructuration et de propagation d'objets:

    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
la source
Peut-être pourriez-vous expliquer ce que cela fait et comment cela fonctionne?
Jon Adams
@JeffLowery si c'était un tableau d'objets? Dois-je faire un foreach ou puis-je faire avec la propagation aussi?
user3808307
@ user3808307 Vous pouvez faire une propagation sur un tableau et des objets dans le tableau.
Jeff Lowery
7

Personnellement, le moyen le plus efficace pour renommer des clés dans un objet sans implémenter des plugins et des roues très lourds:

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

object = JSON.parse(str);

Vous pouvez également l'envelopper try-catchsi votre objet a une structure non valide. Fonctionne parfaitement :)

Novitoll
la source
12
oups! var objet = {b: '123', 'c': 'bbb'}; var str = JSON.stringify (objet); str = str.replace (/ b / g, 'newKey'); str = str.replace (/ c / g, 'newKey2'); object = JSON.parse (str);
BlondinkaBrain
4
Comment cela se compare-t-il pour la vitesse?
mix3d
16
Exécute également un problème potentiel de quelque part dans les valeurs de données est la même chaîne que l'ancien nom de clé.
mix3d
4
"Fonctionne parfaitement" pour certaines définitions de "parfaitement". Essayez de renommer 'a'dans { a: 1, aa: 2, aaa: 3}ou essayer de renommer un objet avec des valeurs qui ne sont rien de plus que des chaînes ou des nombres ou des booléens, ou essayez avec des références circulaires.
rsp
1
si l'objet contient une fonction ou un objet date, cela ne fonctionnera pas
ikhsan
7

Pour ajouter un préfixe à chaque clé:

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
la source
5

Je ferais quelque chose comme ça:

function renameKeys(dict, keyMap) {
  return _.reduce(dict, function(newDict, val, oldKey) {
    var newKey = keyMap[oldKey] || oldKey
    newDict[newKey] = val 
    return newDict
  }, {})
}
tldr
la source
3
Faites une version mise à jour de votre solution qui ne transforme pas les clés statiques en 'non définies' ici: gist.github.com/diasdavid/f8997fb0bdf4dc1c3543 Néanmoins, cela ne résout toujours pas le problème de vouloir remapper les clés à plusieurs niveaux du JSON objet (remappage de clés imbriquées), idées?
David Dias
4

Je dirais qu'il serait préférable d'un point de vue conceptuel de laisser simplement l'ancien objet (celui du service Web) tel quel et de mettre les valeurs dont vous avez besoin dans un nouvel objet. Je suppose que vous extrayez des champs spécifiques à un moment ou à un autre de toute façon, sinon sur le client, du moins sur le serveur. Le fait que vous ayez choisi d'utiliser des noms de champs identiques à ceux du service Web, uniquement en minuscules, ne change rien à cela. Donc, je vous conseille de faire quelque chose comme ça:

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

Bien sûr, je fais toutes sortes d'hypothèses ici, ce qui n'est peut-être pas vrai. Si cela ne s'applique pas à vous, ou si c'est trop lent (est-ce? Je n'ai pas testé, mais j'imagine que la différence diminue avec l'augmentation du nombre de champs), veuillez ignorer tout cela :)

Si vous ne voulez pas le faire, et que vous ne devez prendre en charge que des navigateurs spécifiques, vous pouvez également utiliser les nouveaux getters pour renvoyer également "majuscule (champ)": voir http://robertnyman.com/2009/05/28 / getters-and-setters-with-javascript-code-samples-and-demos / et les liens sur cette page pour plus d'informations.

ÉDITER:

Incroyablement, c'est aussi presque deux fois plus rapide, au moins sur mon FF3.5 au travail. Voir: http://jsperf.com/spiny001

Épineux normand
la source
Merci beaucoup pour vos efforts mais le cas d'utilisation ne manipule pas les objets statiques car leur structure et leur profondeur sont inconnues. Donc, si possible, je voudrais m'en tenir à la portée initiale de la question.
Jean Vincent
4

Bien que cela ne donne pas exactement une meilleure solution pour renommer une clé, il fournit un moyen ES6 rapide et facile de renommer toutes les clés d'un objet sans muter les données qu'elles contiennent.

let b = {a: ["1"], b:["2"]};
Object.keys(b).map(id => {
  b[`root_${id}`] = [...b[id]];
  delete b[id];
});
console.log(b);
Tudor Morar
la source
3
Veuillez utiliser le lien d' édition pour expliquer comment ce code fonctionne et ne pas simplement donner le code, car une explication est plus susceptible d'aider les futurs lecteurs. Voir aussi Comment répondre . source
Jed Fox
Je suis d'accord. Il ne répond pas spécifiquement à la question mais il m'a aidé à faire le travail comme solution pour renommer toutes les clés de l'objet. Peut-être que j'ai raté le sujet ..
Tudor Morar
3

Certaines des solutions répertoriées sur cette page ont des effets secondaires:

  1. affecter la position de la clé dans l'objet, en l'ajoutant en bas (si cela vous intéresse)
  2. ne fonctionnerait pas dans IE9 + (encore une fois, si cela vous importe)

Voici une solution qui conserve la position de la clé au même endroit et est compatible dans IE9 +, mais doit créer un nouvel objet et peut ne pas être la solution la plus rapide:

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

Remarque: IE9 peut ne pas prendre en charge forEach en mode strict

Jasdeep Khalsa
la source
2

Voici un exemple pour créer un nouvel objet avec des clés renommées.

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

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

Encore une autre façon avec la méthode REDUCE la plus puissante .

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
la source
1

Ceci est une petite modification que j'ai apportée à la fonction de bombardier; Pour pouvoir prendre un tableau d'objets au lieu d'un objet seul et vous pouvez également activer l'index. les "clés" peuvent également être attribuées par un tableau

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

tester

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
la source
1

Votre chemin est optimisé, à mon avis. Mais vous vous retrouverez avec des clés réorganisées. La clé nouvellement créée sera ajoutée à la fin. Je sais que vous ne devriez jamais vous fier à l'ordre des clés, mais si vous devez le conserver, vous devrez parcourir toutes les clés et construire un nouvel objet un par un, en remplaçant la clé en question pendant ce processus.

Comme ça:

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
la source
0
  • Vous pouvez utiliser un utilitaire pour gérer cela.
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
la source
0

essayez-le dans votre éditeur préféré <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
la source