JavaScript: klucz zmiany nazwy obiektu

299

Czy istnieje sprytny (tj. Zoptymalizowany) sposób zmiany nazwy klucza w obiekcie javascript?

Niezoptymalizowanym sposobem byłoby:

o[ new_key ] = o[ old_key ];
delete o[ old_key ];
Jean Vincent
źródło
14
Co rozumiesz przez „zoptymalizowany”? Nie sądzę, że może być bardziej zwięzły; nie ma wbudowanej operacji zmiany nazwy.
Pointy
9
To wszystko, co możesz uzyskać. W mojej aplikacji martwiłbym się innymi sprawami. A tak przy okazji, masz do czynienia z przedmiotami, a nie tablicami. W JavaScript nie ma tablic asocjacyjnych (w ścisłym tego słowa znaczeniu).
Felix Kling
3
@Jean Vincent: Czy to tak wolno?
Felix Kling
8
jest to najbardziej zoptymalizowana i podstawowa wersja
Livingston Samuel,
4
Twoja wersja jest najszybsza we wszystkich współczesnych przeglądarkach oprócz safari, przykładowa skrzynka testowa @ jsperf.com/livi-006
Livingston Samuel

Odpowiedzi:

193

Uważam, że najbardziej kompletnym (i poprawnym) sposobem na zrobienie tego jest:

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

Ta metoda zapewnia zachowanie właściwości o zmienionej nazwie w stosunku do właściwości oryginalnej.

Wydaje mi się również, że możliwość zawinięcia tego w funkcję / metodę i wstawienia go Object.prototypejest nieistotna z punktu widzenia twojego pytania.

Valeriu Paloş
źródło
1
To dobre rozwiązanie tylko dla ES5 z prawdziwym plusem zachowania wszystkich właściwości. Nie jest to więc „zoptymalizowane”, ale zdecydowanie dokładniejsze w ES5.
Jean Vincent
6
cieszył się pewnym sceptycyzmem w stosunku do pisania „rzeczy” z „błędnymi wynikami”, ale oczywiście było to spowodowane tym, że osoba zadała pytanie. to jest poprawna odpowiedź moim zdaniem.
Bent Cardan,
3
W razie potrzeby uważam, że to szczególne użycie ES5 jest rozwiązaniem IE9 i nowszym.
Scott Stafford,
1
Sugeruję dodanie sprawdzania poprawności istnienia o.old_key. Zmiana: if (old_key !== new_key)na: if (old_key !== new_key && o[old_key])
Jose
100

Możesz zawinąć pracę w funkcję i przypisać ją do Objectprototypu. Być może użyj płynnego stylu interfejsu, aby umożliwić przepływ wielu nazw.

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 Specyficzny

Chciałbym, żeby składnia nie była tak skomplikowana, ale zdecydowanie miło jest mieć większą kontrolę.

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
źródło
4
@ChaosPandion przepraszam za to, ale naprawdę mam dość kodu JS opartego na błędach, obecnie piszę Przewodnik ( github.com/BonsaiDen/JavaScript-Garden ) o wszystkich dziwactwach (w tym tym, które już naprawiłeś), to mogło wprawić mnie w jakiś tryb rantowania;) (teraz usunąłem -1)
Ivo Wetzel
1
@Ivo - Czy możesz wyjaśnić, dlaczego uważasz, że przedłużanie prototypu jest złe? Wykonano prototypy do przedłużania dziecka!
ChaosPandion
5
@Ivo - Zgadzam się, że jest to ryzykowne, ale nic nie powstrzyma mnie przed rozszerzeniem prototypu, jeśli uważam, że prowadzi to do bardziej ekspresyjnego kodu. Chociaż pochodzę z sposobu myślenia ECMAScript 5, w którym można oznaczyć właściwość jako niepoliczalną za pośrednictwem Object.defineProperty.
ChaosPandion 10.01.11
3
@Ivo - Jeśli dobrą praktyką jest zawsze hasOwnPropertysprawdzanie właściwości i for ... inpętli, to dlaczego ma to znaczenie, jeśli rozszerzamy Object?
David Tang
2
Ten kod działa, ale zastrzeżeniem jest to, że jeśli nowa nazwa klucza już istnieje, jego wartość spadnie: jsfiddle.net/ryurage/B7x8x
Brandon Minton
83

Jeśli mutujesz obiekt źródłowy, ES6 może to zrobić w jednej linii.

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

Lub dwie linie, jeśli chcesz utworzyć nowy obiekt.

const newObject = {};
delete Object.assign(newObject, o, {[newKey]: o[oldKey] })[oldKey];
nverba
źródło
5
Spróbuj: usuń Object.assign (o, {newKey: o.oldKey}). OldKey; Pracował dla mnie dobrze
Tim
2
Dlaczego []wokół jest kwadratowa klamra newKey? Rozwiązanie działa bez tego.
Soumya Kanti
2
Ok - dostałem odpowiedź. To funkcja ES6. Dla każdego, kto natknął się na to, jak ja, oto dobra odpowiedź .
Soumya Kanti
1
Ta odpowiedź byłaby silniejsza, gdyby nazwy zmiennych były bardziej szczegółowe.
lowcrawler
1
Być może? Przepraszam, zapomniałem o tym, co myślałem z moim początkowym komentarzem.
lowcrawler
42

W przypadku, gdy ktoś musi zmienić nazwę listy właściwości:

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

Stosowanie:

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
źródło
2
należy zaakceptować odpowiedź, działała świetnie w moim przypadku użycia. Musiałem przekonwertować odpowiedź API, która poprzedza nazwy krajów niepotrzebnym łańcuchem. Przekształcił obiekt w tablicę kluczy i zmapował każdy klucz metodą podłańcuchową i zwrócił nowy obiekt z nowym kluczem i wartością indeksowaną według oryginalnego klucza
Akin Hwan
1
Jest to miłe, ponieważ nie usuwa starego klucza. Usunięcie starego klucza spowoduje całkowite usunięcie klucza z obiektu, jeśli nazwa klucza się nie zmieniła. W moim przykładzie konwertowałem my_object_propertyna myObjectProperty, ale jeśli moja właściwość była jednosłowa, klucz został usunięty. +1
plan
25

Chciałbym po prostu użyć tej ES6(ES2015)drogi!

musimy nadążać za duchem czasu!

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

skrót ekranu demonstracyjnego

xgqfrms-gildata
źródło
1
Znalazłem to jako czystsze rozwiązanie, gdy chcesz przekształcić zniekształcony obiekt w zupełnie nowy obiekt zrestrukturyzowany.
NJInamdar
5
Dobry, ale myślę, że const {k1: kA, k2: kB, k3: kC,} = old_obj jest wystarczające? nie ma potrzeby rozprzestrzeniania się (chyba że chcesz kopię old_obj).
Hans
@ Hans, tak masz rację. Ale w tej sytuacji chcę zmienić nazwę niektórych kluczy, więc powinienem je rozpowszechnić.
xgqfrms-gildata
2
Wymaga to znacznie więcej kodu, będzie mniej wydajne i utworzy nowy obiekt. Zmiana nazwy właściwości oznacza mutację obiektu. Nowsze nie zawsze jest lepsze.
Jean Vincent
13

Większość odpowiedzi tutaj nie utrzymuje porządku par klucz-wartość obiektu JS. Jeśli masz na ekranie formę par klucz-wartość obiektu, którą chcesz zmodyfikować, ważne jest na przykład zachowanie kolejności wpisów obiektów.

Sposób zapętlania obiektu JS przez ES6 i zamiany pary klucz-wartość na nową parę ze zmodyfikowaną nazwą klucza wyglądałby mniej więcej tak:

let newWordsObject = {};

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

Rozwiązanie zachowuje kolejność wpisów, dodając nowy wpis w miejsce starego.

afalak
źródło
Idealnie, właśnie tego potrzebowałem
p0358
12

Możesz spróbować 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
źródło
To rozwiązanie działało dla mnie, ale zwracaj uwagę, aby zawsze zwracać zmienną „klucz”. Przykład: if (key === 'oldKey') {return 'newKey'; } klawisz powrotu;
TomoMiha,
12

Odmiana wykorzystująca funkcję destrukcji obiektów i operatora rozprzestrzeniania

    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
źródło
Być może mógłbyś wyjaśnić, co to robi i jak to działa?
Jon Adams,
@JeffLowery co jeśli byłby to szereg obiektów? Czy muszę zrobić foreach, czy mogę też zrobić z spreadem?
user3808307
@ user3808307 Możesz wykonać rozkładanie na tablicy i obiekty w tablicy.
Jeff Lowery
7

Osobiście najskuteczniejszy sposób zmiany nazwy kluczy w obiekcie bez implementacji dodatkowych ciężkich wtyczek i kół:

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

object = JSON.parse(str);

Możesz go również owinąć, try-catchjeśli Twój obiekt ma niepoprawną strukturę. Działa świetnie :)

Novitoll
źródło
12
ooops! var object = {b: '123', 'c': 'bbb'}; var str = JSON.stringify (obiekt); str = str .replace (/ b / g, 'newKey'); str = str .replace (/ c / g, 'newKey2'); object = JSON.parse (str);
BlondinkaBrain
4
Jak to się ma do prędkości?
mix3d
16
Występuje również potencjalny problem gdzieś w wartościach danych jest ten sam ciąg znaków, co stara nazwa klucza.
mix3d
4
„Działa idealnie” w przypadku niektórych definicji „doskonale”. Spróbuj zmienić nazwę 'a'w { a: 1, aa: 2, aaa: 3}lub spróbuj zmienić nazwę obiektu z wartościami, które są niczym więcej niż ciągami, liczbami lub wartościami logicznymi, albo spróbuj z odwołaniami cyklicznymi.
rsp
1
jeśli obiekt zawiera funkcję lub obiekt daty, to nie zadziała
ikhsan
7

Aby dodać prefiks do każdego klucza:

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
źródło
5

Zrobiłbym coś takiego:

function renameKeys(dict, keyMap) {
  return _.reduce(dict, function(newDict, val, oldKey) {
    var newKey = keyMap[oldKey] || oldKey
    newDict[newKey] = val 
    return newDict
  }, {})
}
tldr
źródło
3
Utworzono zaktualizowaną wersję rozwiązania, które nie przekształca kluczy statycznych w „niezdefiniowane” tutaj: gist.github.com/diasdavid/f8997fb0bdf4dc1c3543 Niemniej jednak nadal nie rozwiązuje problemu konieczności ponownego mapowania kluczy na kilku poziomach JSON obiekt (remapowanie zagnieżdżonych kluczy), pomysły?
David Dias,
4

Powiedziałbym, że z koncepcyjnego punktu widzenia lepiej byłoby po prostu zostawić stary obiekt (ten z serwisu internetowego) w obecnej postaci i umieścić potrzebne wartości w nowym obiekcie. Zakładam, że i tak wyodrębniasz określone pola w tym czy innym miejscu, jeśli nie na kliencie, to przynajmniej na serwerze. Fakt, że zdecydowałeś się używać nazw pól, które są takie same jak te z serwisu internetowego, tylko małe litery, tak naprawdę tego nie zmienia. Radziłbym więc zrobić coś takiego:

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

Oczywiście przyjmuję tutaj różne założenia, co może nie być prawdą. Jeśli to Cię nie dotyczy lub jest zbyt wolne (prawda? Nie testowałem, ale wyobrażam sobie, że różnica maleje wraz ze wzrostem liczby pól), zignoruj ​​to wszystko :)

Jeśli nie chcesz tego robić i musisz obsługiwać tylko określone przeglądarki, możesz również użyć nowych programów pobierających, aby zwrócić również „wielkie litery (pola)”: patrz http://robertnyman.com/2009/05/28 / getters-and-setters-with-javascript-code-samples-and-demos / oraz linki na tej stronie, aby uzyskać więcej informacji.

EDYTOWAĆ:

Niesamowicie, jest to prawie dwa razy szybsze, przynajmniej na moim FF3.5 w pracy. Zobacz: http://jsperf.com/spiny001

Kolczasty Norman
źródło
Bardzo dziękuję za twój wysiłek, ale przypadek użycia nie manipuluje obiektami statycznymi, ponieważ ich struktura i głębokość są nieznane. Jeśli to możliwe, chciałbym trzymać się pierwotnego zakresu pytania.
Jean Vincent
4

Chociaż nie daje to lepszego rozwiązania w kwestii zmiany nazwy klucza, zapewnia on szybki i łatwy sposób zmiany nazwy wszystkich kluczy w obiekcie przy jednoczesnym braku mutacji zawartych w nich danych.

let b = {a: ["1"], b:["2"]};
Object.keys(b).map(id => {
  b[`root_${id}`] = [...b[id]];
  delete b[id];
});
console.log(b);
Tudor Morar
źródło
3
Proszę użyć linku edycyjnego, aby wyjaśnić, jak działa ten kod, a nie tylko podać kod, ponieważ wyjaśnienie może pomóc przyszłym czytelnikom. Zobacz także Jak odpowiedzieć . źródło
Jed Fox
Zgadzam się. Nie odpowiada konkretnie na pytanie, ale pomogło mi wykonać zadanie jako rozwiązanie zmiany nazwy wszystkich kluczy w obiekcie. Może przegapiłem temat ..
Tudor Morar
3

Niektóre rozwiązania wymienione na tej stronie mają pewne skutki uboczne:

  1. wpływa na pozycję klucza w obiekcie, dodając go do dołu (jeśli ma to dla Ciebie znaczenie)
  2. nie działałby w IE9 + (ponownie, jeśli to dla Ciebie ważne)

Oto rozwiązanie, które utrzymuje pozycję klucza w tym samym miejscu i jest kompatybilne z IE9 +, ale musi utworzyć nowy obiekt i może nie być najszybszym rozwiązaniem:

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

Uwaga: IE9 może nie obsługiwać każdego w trybie ścisłym

Jasdeep Khalsa
źródło
2

Oto przykład utworzenia nowego obiektu z kluczami o zmienionych nazwach.

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

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

Jeszcze inny sposób z najbardziej wydajną metodą REDUKCJI .

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
źródło
1

To jest mała modyfikacja, którą wprowadziłem do funkcji pomber; Aby móc wziąć tablicę obiektów zamiast samego obiektu, a także można aktywować indeks. także „Klucze” mogą być przypisywane przez tablicę

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);
Jasp402
źródło
1

Twoja droga jest zoptymalizowana, moim zdaniem. Ale skończysz z uporządkowanymi kluczami. Nowo utworzony klucz zostanie dołączony na końcu. Wiem, że nigdy nie powinieneś polegać na kolejności kluczy, ale jeśli musisz ją zachować, będziesz musiał przejść przez wszystkie klucze i zbudować nowy obiekt jeden po drugim, zastępując dany klucz podczas tego procesu.

Lubię to:

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.
źródło
0
  • Możesz użyć narzędzia do obsługi tego.
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
źródło
0

po prostu spróbuj w swoim ulubionym edytorze <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
źródło