JavaScript:对象重命名键


299

是否有一种巧妙(即优化)的方法来重命名javascript对象中的键?

一种非优化的方式是:

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

14
“优化”是什么意思?我认为它不会比这更简洁。没有“重命名”内置操作。
Pointy

9
那就是你所能得到的。我会担心我的应用程序中的其他问题。顺便说一句,您正在处理对象,而不是数组。JavaScript中没有严格意义上的关联数组。
Felix Kling

3
@让·文森特:这么慢吗?
Felix Kling

8
这是最优化和最基本的版本
Livingston Samuel

4
您的版本是所有现代浏览器中最快的版本,但Safari浏览器除外,示例测试用例@ jsperf.com/livi-006
Livingston Samuel,

Answers:


193

我认为,最完整(正确)的方法是:

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

此方法确保重命名的属性的行为与原始属性相同

另外,在我看来,将其包装到函数/方法中并放入其中的可能性Object.prototype与您的问题无关。


1
这是一个不错的ES5解决方案,具有保留所有属性的真正优势。因此,这不是“优化”的,而是在ES5上绝对更准确。
Jean Vincent

6
有人对Writing_things_with_underscores持怀疑态度,但那当然是由于有人问这个问题。我认为这是正确的回答。
Bent Cardan

3
万一重要,我相信ES5的这种特殊用法是IE9和更高版本的解决方案。
Scott Stafford 2014年

1
我建议添加一个o.old_key存在的验证。更改: if (old_key !== new_key)至: if (old_key !== new_key && o[old_key])
jose

100

您可以将工作包装在一个函数中,然后将其分配给Object原型。也许使用流畅的界面样式可以进行多个重命名流程。

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特定

我希望语法不是那么复杂,但是拥有更多控制权绝对是一件好事。

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对此感到抱歉,但是我真的很厌倦臭虫缠身的JS代码,我目前正在写一本指南(github.com/BonsaiDen/JavaScript-Garden)关于所有怪癖(包括您现在已解决的怪癖),这可能使我进入了某种大声疾呼的模式;)(现在已删除-1)
Ivo Wetzel

1
@Ivo-您能解释一下为什么您觉得扩展原型不好吗?原型是为扩展宝宝而设计的!
ChaosPandion 2011年

5
@Ivo-我同意这样做是有风险的,但是如果我认为原型会导致更具表现力的代码,那么没有什么可以阻止我扩展原型。尽管我来自ECMAScript 5心态,但是您可以通过将该属性标记为不可枚举Object.defineProperty
ChaosPandion 2011年

3
@Ivo-如果始终使用hasOwnProperty属性来检查属性和在for ... in循环内是一种适当的做法,那么为什么扩展Object为何这么重要?
David Tang

2
这段代码有效,但需要注意的是,如果新的密钥名称已经存在,则其值将下降:jsfiddle.net/ryurage/B7x8x
Brandon Minton

83

如果要更改源对象,则ES6可以一行完成。

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

如果要创建一个新对象,则需要两行。

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

5
试试:删除Object.assign(o,{newKey:o.oldKey})。oldKey; 为我工作得很好
蒂姆(Tim)

2
为什么[]周围有方括号newKey?该解决方案实际上无需解决该问题。
Soumya Kanti,

2
好的-我知道了。这是ES6功能。对于像我这样偶然发现的其他人,这是一个很好的答案
Soumya Kanti,

1
如果您的变量名更详细,则此答案会更强。
lowcrawler,

1
也许?抱歉,我最初的评论忘记了我的想法。
lowcrawler

42

如果有人需要重命名属性列表:

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

用法:

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
这应该被接受,对于我的用例来说效果很好。我必须转换一个API响应,该响应会在国家/地区名称前添加不必要的字符串。将对象转换为键数组,并使用子字符串方法通过每个键进行映射,并返回一个具有新键和由原始键索引的值的新对象
Akin Hwan

1
很好,因为它不会删除旧密钥。如果键名称未更改,则删除旧键将完全从对象中删除键。在我的示例中,我正在转换my_object_propertymyObjectProperty,但是,如果我的属性是单字的,那么该键将被删除。+1
blueprintchris

25

我只想使用ES6(ES2015)方式!

我们需要与时俱进!

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

演示屏幕快捷方式


1
当您要将畸形的对象转换为全新的重组对象时,找到了一种更清洁的解决方案。
NJInamdar

5
好一个,但是我认为const {k1:kA,k2:kB,k3:kC,} = old_obj就足够了吗?无需传播(除非您想要old_obj的副本)。
汉斯,

@汉斯,是的,你是对的。但是在这种情况下,我想重命名一些密钥,所以我应该传播它们。
xgqfrms-gildata

2
这需要更多的代码,效率会降低,并创建一个新的对象。重命名属性意味着对对象进行突变。更新并不总是更好。
让·文森特


13

这里的大多数答案都无法维持JS对象键值对的顺序。例如,如果您要在屏幕上使用某种形式的对象键-值对,则保留对象条目的顺序很重要。

ES6循环遍历JS对象并将键值对替换为具有修改后的键名的新对的方式将类似于:

let newWordsObject = {};

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

该解决方案通过将新条目添加到旧条目的位置来保留条目的顺序。


完美,这就是我所需要的
p0358 '19

12

您可以尝试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>


该解决方案对我有用,但要注意始终返回“键”变量。示例:if(key ==='oldKey'){return'newKey'; }返回键;
TomoMiha

12

使用对象解构和散布运算符的变体:

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

也许您可以解释这是怎么做的以及它是如何工作的?
乔恩·亚当斯

@JeffLowery如果是对象数组怎么办?我需要做一个foreach还是我也可以做点传播?
user3808307

@ user3808307您可以在数组以及数组中的对象上进行扩展。
杰夫·洛里

7

就个人而言,最有效的方法是重命名对象中的键,而无需实现额外的繁琐插件和滚轮:

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

object = JSON.parse(str);

try-catch如果对象的结构无效,也可以将其包装。完美的工作:)


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

4
如何比较速度?
mix3d

16
在数据值中某处的潜在问题是与旧键名相同的字符串。
mix3d

4
对于“完全”的某些定义,“完美工作”。尝试重命名'a'{ a: 1, aa: 2, aaa: 3}或尝试用超过字符串,数字或布尔值是什么,值重命名对象,或循环引用的尝试。
rsp

1
如果对象包含函数或日期对象,则将无法使用
ikhsan

7

为每个键添加前缀:

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

我会做这样的事情:

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

3
制作了解决方案的更新版本,该解决方案不会在此处将静态键转换为“未定义”:gist.github.com/diasdavid/f8997fb0bdf4dc1c3543 不过,它仍然没有解决要在JSON的多个级别重新映射键的问题。对象(重新映射嵌套键),想法?
大卫·迪亚斯

4

我要说的是,从概念的角度来看,最好将旧对象(Web服务中的那个)保持原样,然后将所需的值放在新对象中。我假设您无论如何都在某一点或另一点提取特定字段,如果不在客户端上,那么至少在服务器上。您选择使用与Web服务中的字段名相同的字段名(仅使用小写字母)实际上并没有真正改变这一点。所以,我建议做这样的事情:

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

当然,我在这里做出各种假设,但这可能不是正确的。如果这不适用于您,或者速度太慢(是吗?我尚未测试,但是我想随着字段数的增加,差异会变小),请忽略所有这些:)

如果您不想这样做,并且只需要支持特定的浏览器,则还可以使用新的getter来返回“ uppercase(field)”:请参见http://robertnyman.com/2009/05/28 / getters-and-setters-with-javascript-code-samples-and-demos /以及该页面上的链接以获取更多信息。

编辑:

令人难以置信的是,这也几乎快了两倍,至少在我的FF3.5上是这样。请参阅:http//jsperf.com/spiny001


非常感谢您的努力,但是用例无法操纵静态对象,因为静态对象的结构和深度未知。因此,如果可能的话,我想坚持这个问题的原始范围。
让·文森特

4

尽管这不能为重命名提供更好的解决方案,但它提供了一种快速,简便的ES6方法,可以重命名对象中的所有键,而不会使它们包含的数据发生变异。

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

3
请使用编辑链接来解释此代码的工作原理,而不仅仅是给出代码,因为这样的解释更有可能对未来的读者有所帮助。另请参阅“ 如何回答”消息来源
杰德·福克斯

我同意。它没有具体回答问题,但是它帮助我完成了工作,作为重命名对象中所有键的解决方案。也许我错过了话题..
都铎莫勒

3

此页面上列出的某些解决方案有一些副作用:

  1. 影响键在对象中的位置,将其添加到底部(如果这很重要)
  2. 在IE9 +中将无法使用(同样,如果这对您很重要)

这是一个将键的位置保持在同一位置并与IE9 +兼容的解决方案,但是必须创建一个新对象,可能不是最快的解决方案:

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

请注意:IE9在严格模式下可能不支持forEach


2

这是一个使用重命名键创建新对象的示例。

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

最强大的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);


1

这是我对pomber的功能所做的小修改;为了能够采用对象数组而不是单独的对象,还可以激活索引。也可以通过数组分配“键”

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

测试

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

我认为您的方式已经过优化。但是您最终将获得重新排序的密钥。新创建的密钥将添加到末尾。我知道您永远不应该依赖键顺序,但是如果您需要保留键顺序,则将需要遍历所有键并一个接一个地构造新对象,并在此过程中替换有问题的键。

像这样:

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
  • 您可以使用实用程序来处理此问题。
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

只需在您喜欢的编辑器中尝试<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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.