JavaScript:对象重命名键

484

有没有一种聪明(即优化)的方式来重命名JavaScript对象中的键?

非优化的方法如下:

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

21
“optimized”是什么意思?我认为这已经非常简洁了,没有内置的“重命名”操作。 - Pointy
12
这就是你能得到的全部。我会担心我的申请中的其他事情。另外,你正在处理对象而不是数组。在JavaScript中(严格意义上),没有关联数组。 - Felix Kling
5
@Jean Vincent: 它速度真的很慢吗? - Felix Kling
12
这是最优化和基础版。 - Livingston Samuel
6
除Safari以外,你的版本在所有现代浏览器中都是最快的。样例测试案例请访问 http://jsperf.com/livi-006。 - Livingston Samuel
显示剩余12条评论
36个回答

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;
}, {});

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

我希望你能帮我做到这一点

const originalObj = {
  a: 1,
  b: 2,
  c: 3, // need replace this 'c' key into 'd'
};

const { c, ...rest } = originalObj;

const newObj = { ...rest, d: c };

console.log({ originalObj, newObj });

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community
这不是对问题的回答。一旦您拥有足够的 声望,您将能够评论任何帖子; 相反,[提供不需要问者澄清的答案] (https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead)。- 来自审核 - L.Dutch

1

我采用了自己的方式,改编了 Mulhoon的好文章typescript post,以便更改多个键:

const renameKeys = <
    TOldKey extends keyof T,
    TNewkey extends string,
    T extends Record<string, unknown>
>(
  keys:  {[ key: string]: TNewkey extends TOldKey ? never : TNewkey },
  obj: T
) => Object
    .keys(obj)
    .reduce((acc, key) => ({
        ...acc,
        ...{ [keys[key] || key]: obj[key] }
    }), {});

renameKeys({id: 'value', name: 'label'}, {id: 'toto_id', name: 'toto', age: 35});

我有一个可用的示例:var arr = { id: 'toto_id', name: 'toto', age: 35 };var arr2 = renameKeys({ id: 'value', name: 'label' }, arr);console.log(arr2)。如何将其详细说明以使用“对象数组”而不是单个对象? - Timo

1

如果有人需要重命名对象的键:

 
  const renameKeyObject = (obj, oldKey, newKey) => {
      if (oldKey === newKey) return obj;
      Object.keys(obj).forEach((key) => {
          if (key === oldKey) {
              obj[newKey] = obj[key];
              delete obj[key];
            } else if (obj[key] !== null && typeof obj[key] === "object") {
                obj[key] = renameKeyObject(obj[key], oldKey, newKey);
              }
            });
  return obj;
};


1
您可以使用实用程序来处理此事。
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' };


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;

1
来吧,到了2023年我们应该更明白了!
很多答案完全忽略了内存和保留引用的问题...前者是你实际上对代码进行“优化”的地方。
将扩展运算符用于匿名对象不仅可以将引用与项目的其余部分解耦,还会创建与对象大小相当的垃圾。在你的像素完美的视差动画中看到抖动和卡顿?那就是垃圾回收为你清理掉的.. 'Object.assign'可以解决引用问题,但它不能同时复制所有属性并遍历整个对象。
回到基础吧!
重新赋值 -> 删除是最优的,因为你只需要分配一个新指针,同时删除旧指针。
如果你真的需要一行代码,只需加上括号..
delete (o[new_key] = o[old_key], o)[old_key]

虽然两行代码更易读...
并且不要依赖哈希映射的插入顺序。对于“较小”的列表,使用数组和指针;对于较大的列表,使用链表。

1
谢谢,我认为这是很长时间以来最有用的答案。我不知道删除操作会立即回收内存,而不依赖和等待垃圾回收器。这使得它更有效地优化了,这也是问题的目标所在。 - Jean Vincent
1
谢谢,我认为这是很长时间以来最有用的答案。我不知道删除操作会立即回收内存,而不是依赖和等待垃圾回收器。这使得它更有效地优化了,这也是问题的目标所在。 - undefined
1
我看不出这个答案与前面的答案有什么不同。 - starball
不是,但解释是。 - zergski
@JeanVincent 很乐意帮忙!可能听起来有点啰嗦,但对某些人来说可能是有用的信息。内存被标记为已删除,这是任何内存的标准操作,可以被覆盖,但垃圾回收机制会忽略它。很多时候它仍然分配给你的应用程序。似乎取决于缓冲区所在的位置,堆还是栈,在这方面我有点不确定。V8团队做了很多魔法,我还没有完全理解他们的一些技巧。 - zergski
我做了一些研究,不幸的是没有找到有关删除使GC绕过的信息。你能指引我找到这方面的来源吗?谢谢。 - Jean Vincent

0

这样做会有任何问题吗?

someObject = {...someObject, [newKey]: someObject.oldKey}
delete someObject.oldKey

如果需要,这可以被包装在一个函数中:

const renameObjectKey = (object, oldKey, newKey) => {
    // if keys are the same, do nothing
    if (oldKey === newKey) return;
    // if old key doesn't exist, do nothing (alternatively, throw an error)
    if (!object.oldKey) return;
    // if new key already exists on object, do nothing (again - alternatively, throw an error)
    if (object.newKey !== undefined) return;

    object = { ...object, [newKey]: object[oldKey] };
    delete object[oldKey];

    return { ...object };
};

// in use
let myObject = {
    keyOne: 'abc',
    keyTwo: 123
};

// avoids mutating original
let renamed = renameObjectKey(myObject, 'keyTwo', 'renamedKey');

console.log(myObject, renamed);
// myObject
/* {
    "keyOne": "abc",
    "keyTwo": 123,
} */

// renamed
/* {
    "keyOne": "abc",
    "renamedKey": 123,
} */

分配到一个新的对象中.. 分配内存.. 意味着旧的对象现在不再使用,需要进行垃圾回收,同时它也解除了引用关系,所以如果你更新了新的对象,旧的对象将不会被更新。 - zergski

0

在寻找许多答案后,这是我最好的解决方案:

const renameKey = (oldKey, newKey) => {
  _.reduce(obj, (newObj, value, key) => {
    newObj[oldKey === key ? newKey : key] = value
    return newObj
  }, {})
}

它不是替换原始键,而是构建一个新对象,这很清楚。 问题中的方法虽然可行,但会改变对象的顺序,因为它将新的键值添加到最后。


即使使用reduce..新对象===更多内存,而且你还要遍历整个内容。为什么呢? - zergski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接