I have some structured JSON data like so. Let's assume this is interchangeable, via JSON.parse()
:
我有一些结构化的JSON数据。让我们假设这是可以互换的,通过JSON.parse():
[
{
"title": "pineapple",
"uid": "ab982d34c98f"
},
{
"title": "carrots",
"uid": "6f12e6ba45ec"
}
]
I need it to look like this, remapping title
to name
, and uid
to id
with the result:
我需要它看起来像这样,将标题重新映射到名称,并将uid重命名为结果:
[
{
"name": "pineapple",
"id": "ab982d34c98f"
},
{
"name": "carrots",
"id": "6f12e6ba45ec"
}
]
The most obvious way of doing it is like this:
最明显的做法是这样的:
str = '[{"title": "pineapple","uid": "ab982d34c98f"},{"title": "carrots", "uid": "6f12e6ba45ec"}]';
var arr = JSON.parse(str);
for (var i = 0; i<arr.length; i++) {
arr[i].name = arr[i].title;
arr[i].id = arr[i].uid;
delete arr[i].title;
delete arr[i].uid;
}
str = '[{"title": "pineapple","uid": "ab982d34c98f"},{"title": "carrots", "uid": "6f12e6ba45ec"}]';
var arr = JSON.parse(str);
for (var i = 0; i<arr.length; i++) {
arr[i].name = arr[i].title;
arr[i].id = arr[i].uid;
delete arr[i].title;
delete arr[i].uid;
}
$('body').append("<pre>"+JSON.stringify(arr, undefined, 4)+"</pre>");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
...or using something more complex (albeit not more efficient) like this.
...或者使用更复杂的东西(尽管效率不高)。
This is all fine and dandy, but what if there were 200,000 objects in the array? This is a lot of processing overhead.
这一切都很好,但是如果阵列中有200,000个对象怎么办?这是很多处理开销。
Is there a more efficient way to remap a key name? Possibly without looping through the entire array of objects? If your method is more efficient, please provide proof/references.
是否有更有效的方法来重新映射密钥名称?可能没有遍历整个对象数组?如果您的方法更有效,请提供证明/参考。
7 个解决方案
#1
24
As I already mentioned in the comments, if you can make certain assumptions about the values of the objects, you could use a regular expression to replace the keys, for example:
正如我在评论中已经提到的,如果您可以对对象的值做出某些假设,则可以使用正则表达式来替换键,例如:
str = str.replace(/"title":/g, '"name":');
It's not as "clean", but it might get the job done faster.
它不像“干净”,但可能会更快地完成工作。
If you have to parse the JSON anyway, a more structured approach would be to pass a reviver function to JSON.parse
and you might be able to avoid an additional pass over the array. This probably depends on how engine implement JSON.parse
though (maybe they parse the whole string first and then make a second pass with the reviver function, in which case you wouldn't get any advantage).
如果你必须解析JSON,更结构化的方法是将一个reviver函数传递给JSON.parse,你可能可以避免对数组进行额外的传递。这可能取决于引擎如何实现JSON.parse(可能它们首先解析整个字符串,然后使用reviver函数进行第二次传递,在这种情况下,您将无法获得任何优势)。
var arr = JSON.parse(str, function(prop, value) {
switch(prop) {
case "title":
this.name = value;
return;
case "uid":
this.id = value;
return;
default:
return value;
}
});
Benchmarks, using the Node.js script below to test 3 times:
基准测试,使用下面的Node.js脚本测试3次:
1389822740739: Beginning regex rename test
1389822740761: Regex rename complete
// 22ms, 22ms, 21ms
1389822740762: Beginning parse and remap in for loop test
1389822740831: For loop remap complete
// 69ms, 68ms, 68ms
1389822740831: Beginning reviver function test
1389822740893: Reviver function complete
// 62ms, 61ms, 60ms
It appears as if the regex (in this case) is the most efficient, but be careful when trying to parse JSON with regular expressions.
看起来好像正则表达式(在这种情况下)是最有效的,但在尝试使用正则表达式解析JSON时要小心。
Test script, loading 100,230 lines of the OP's sample JSON:
测试脚本,加载OP的样本JSON的100,230行:
fs = require('fs');
fs.readFile('test.json', 'utf8', function (err, data) {
if (err) {
return console.log(err);
}
console.log(new Date().getTime() + ": Beginning regex rename test");
var str = data.replace(/"title":/g, '"name":');
str = str.replace(/"uid":/g, '"id":');
JSON.parse(str);
console.log(new Date().getTime() + ": Regex rename complete");
console.log(new Date().getTime() + ": Beginning parse and remap in for loop test");
var arr = JSON.parse(data);
for (var i = 0; i < arr.length; i++) {
arr[i].name = arr[i].title;
arr[i].id = arr[i].uid;
delete arr[i].title;
delete arr[i].uid;
}
console.log(new Date().getTime() + ": For loop remap complete");
console.log(new Date().getTime() + ": Beginning reviver function test");
var arr = JSON.parse(data, function (prop, value) {
switch (prop) {
case "title":
this.name = value;
return;
case "uid":
this.id = value;
return;
default:
return value;
}
});
console.log(new Date().getTime() + ": Reviver function complete");
});
#2
8
Asked this question a long time ago, and since then, I've grown acustomed to using Array.prototype.map() to get the job done, more for stability and cleanliness of code than performance. While it's certainly not the most performant, it looks great:
很久以前问过这个问题,从那以后,我已经习惯了使用Array.prototype.map()来完成工作,更多的是为了稳定性和清洁代码而不是性能。虽然它当然不是最高效的,但它看起来很棒:
var repl = orig.map(function(obj) {
return {
name: obj.title,
id: obj.uid
}
})
If you need a more flexible (and ES6-compatible function), try:
如果您需要更灵活(和ES6兼容的功能),请尝试:
let replaceKeyInObjectArray = (a, r) => a.map(o =>
Object.keys(o).map((key) => ({ [r[key] || key] : o[key] })
).reduce((a, b) => Object.assign({}, a, b)))
e.g.
例如
const arr = [{ abc: 1, def: 40, xyz: 50 }, { abc: 1, def: 40, xyz: 50 }, { abc: 1, def: 40, xyz: 50 }]
const replaceMap = { "abc": "yyj" }
replaceKeyInObjectArray(arr, replaceMap)
/*
[
{
"yyj": 1,
"def": 40,
"xyz": 50
},
{
"yyj": 1,
"def": 40,
"xyz": 50
},
{
"yyj": 1,
"def": 40,
"xyz": 50
}
]
*/
#3
5
Here's another take on the OP's suggestion to use map()
for clarity (not performance).
这是对OP的建议的另一种看法,即使用map()来表示清晰度(而非性能)。
var newItems = items.map(item => ({
name: item.title,
id: item.uid
}));
This uses ES6 arrow functions and the shortcut syntaxes that are possible when there's only one parm passed to the function and only one statement in the body of the function.
这使用ES6箭头函数和当只有一个parm传递给函数并且函数体中只有一个语句时可能的快捷语法。
Depending on your history with lambda expressions in various languages, this form may or may not resonate with you.
根据您使用各种语言的lambda表达式的历史记录,此表单可能会或可能不会与您产生共鸣。
Be careful when returning an object literal in the arrow function shortcut syntax like this. Don't forget the additional parens around the object literal!
使用箭头函数快捷方式语法返回对象文字时要小心。不要忘记对象字面周围的额外parens!
#4
2
If you want to make it a little more reusable. Maybe this is a decent approach.
如果你想让它更可重复使用。也许这是一个不错的方法。
function rekey(arr, lookup) {
for (var i = 0; i < arr.length; i++) {
var obj = arr[i];
for (var fromKey in lookup) {
var toKey = lookup[fromKey];
var value = obj[fromKey];
if (value) {
obj[toKey] = value;
delete obj[fromKey];
}
}
}
return arr;
}
var arr = [{ apple: 'bar' }, { apple: 'foo' }];
var converted = rekey(arr, { apple: 'kung' });
console.log(converted);
#5
1
var jsonObj = [/*sample array in question*/ ]
Based on different benchmarks discussed below, fastest solution is native for:
根据下面讨论的不同基准,最快的解决方案原生于:
var arr = [];
for(var i = 0, len = jsonObj .length; i < len; i++) {
arr.push( {"name": jsonObj[i].title, "id" : jsonObj[i].uid});
}
I think alternatively without using a frameworks this will be option 2:
我认为,如果不使用框架,这将是选项2:
var arr = []
jsonObj.forEach(function(item) { arr.push({"name": item.title, "id" : item.uid }); });
There is always debate between using navite and non-navite functions. If I remember correctly lodash argued they were faster than underscore because the use non-native functions for key operations.
使用导航功能和非导航功能之间始终存在争议。如果我没记错的话,lodash认为他们比下划线更快,因为使用非原生函数进行关键操作。
However different browsers will produce sometimes very different results. I always looked for the best average.
但是,不同的浏览器有时会产生非常不同的结我总是寻找最好的平均值。
For benchmarks you can take a look at this:
对于基准测试,您可以看一下:
http://jsperf.com/lo-dash-v1-1-1-vs-underscore-v1-4-4/8
http://jsperf.com/lo-dash-v1-1-1-vs-underscore-v1-4-4/8
#6
1
Using ES6:
使用ES6:
const renameFieldInArrayOfObjects = (arr, oldField, newField) => {
return arr.map(s => {
return Object.keys(s).reduce((prev, next) => {
if(next === oldField) {
prev[newField] = s[next]
} else {
prev[next] = s[next]
}
return prev
}, {})
})
}
Using ES7:
使用ES7:
const renameFieldInArrayOfObjects = (arr, oldField, newField) => {
return arr.map(s => {
return Object.keys(s).reduce((prev, next) => {
return next === oldField
? {...prev, [newField]: s[next]}
: {...prev, [next]: s[next]}
}, {})
})
}
#7
1
You can use an npm package named node-data-transform.
您可以使用名为node-data-transform的npm包。
Your data :
你的数据:
const data = [
{
title: 'pineapple',
uid: 'ab982d34c98f',
},
{
title: 'carrots',
uid: '6f12e6ba45ec',
},
];
Your mapping :
你的映射:
const map = {
item: {
name: 'title',
id: 'uid',
},
};
And use the package :
并使用包:
const DataTransform = require("node-json-transform").DataTransform;
const dataTransform = DataTransform(data, map);
const result = dataTransform.transform();
console.log(result);
Result :
结果:
[
{
name: 'pineapple',
id: 'ab982d34c98f'
},
{
name: 'carrots',
id: '6f12e6ba45ec'
}
]
Maybe it's not the best way for performance, but it's quite elegant.
也许这不是性能的最佳方式,但它非常优雅。
#1
24
As I already mentioned in the comments, if you can make certain assumptions about the values of the objects, you could use a regular expression to replace the keys, for example:
正如我在评论中已经提到的,如果您可以对对象的值做出某些假设,则可以使用正则表达式来替换键,例如:
str = str.replace(/"title":/g, '"name":');
It's not as "clean", but it might get the job done faster.
它不像“干净”,但可能会更快地完成工作。
If you have to parse the JSON anyway, a more structured approach would be to pass a reviver function to JSON.parse
and you might be able to avoid an additional pass over the array. This probably depends on how engine implement JSON.parse
though (maybe they parse the whole string first and then make a second pass with the reviver function, in which case you wouldn't get any advantage).
如果你必须解析JSON,更结构化的方法是将一个reviver函数传递给JSON.parse,你可能可以避免对数组进行额外的传递。这可能取决于引擎如何实现JSON.parse(可能它们首先解析整个字符串,然后使用reviver函数进行第二次传递,在这种情况下,您将无法获得任何优势)。
var arr = JSON.parse(str, function(prop, value) {
switch(prop) {
case "title":
this.name = value;
return;
case "uid":
this.id = value;
return;
default:
return value;
}
});
Benchmarks, using the Node.js script below to test 3 times:
基准测试,使用下面的Node.js脚本测试3次:
1389822740739: Beginning regex rename test
1389822740761: Regex rename complete
// 22ms, 22ms, 21ms
1389822740762: Beginning parse and remap in for loop test
1389822740831: For loop remap complete
// 69ms, 68ms, 68ms
1389822740831: Beginning reviver function test
1389822740893: Reviver function complete
// 62ms, 61ms, 60ms
It appears as if the regex (in this case) is the most efficient, but be careful when trying to parse JSON with regular expressions.
看起来好像正则表达式(在这种情况下)是最有效的,但在尝试使用正则表达式解析JSON时要小心。
Test script, loading 100,230 lines of the OP's sample JSON:
测试脚本,加载OP的样本JSON的100,230行:
fs = require('fs');
fs.readFile('test.json', 'utf8', function (err, data) {
if (err) {
return console.log(err);
}
console.log(new Date().getTime() + ": Beginning regex rename test");
var str = data.replace(/"title":/g, '"name":');
str = str.replace(/"uid":/g, '"id":');
JSON.parse(str);
console.log(new Date().getTime() + ": Regex rename complete");
console.log(new Date().getTime() + ": Beginning parse and remap in for loop test");
var arr = JSON.parse(data);
for (var i = 0; i < arr.length; i++) {
arr[i].name = arr[i].title;
arr[i].id = arr[i].uid;
delete arr[i].title;
delete arr[i].uid;
}
console.log(new Date().getTime() + ": For loop remap complete");
console.log(new Date().getTime() + ": Beginning reviver function test");
var arr = JSON.parse(data, function (prop, value) {
switch (prop) {
case "title":
this.name = value;
return;
case "uid":
this.id = value;
return;
default:
return value;
}
});
console.log(new Date().getTime() + ": Reviver function complete");
});
#2
8
Asked this question a long time ago, and since then, I've grown acustomed to using Array.prototype.map() to get the job done, more for stability and cleanliness of code than performance. While it's certainly not the most performant, it looks great:
很久以前问过这个问题,从那以后,我已经习惯了使用Array.prototype.map()来完成工作,更多的是为了稳定性和清洁代码而不是性能。虽然它当然不是最高效的,但它看起来很棒:
var repl = orig.map(function(obj) {
return {
name: obj.title,
id: obj.uid
}
})
If you need a more flexible (and ES6-compatible function), try:
如果您需要更灵活(和ES6兼容的功能),请尝试:
let replaceKeyInObjectArray = (a, r) => a.map(o =>
Object.keys(o).map((key) => ({ [r[key] || key] : o[key] })
).reduce((a, b) => Object.assign({}, a, b)))
e.g.
例如
const arr = [{ abc: 1, def: 40, xyz: 50 }, { abc: 1, def: 40, xyz: 50 }, { abc: 1, def: 40, xyz: 50 }]
const replaceMap = { "abc": "yyj" }
replaceKeyInObjectArray(arr, replaceMap)
/*
[
{
"yyj": 1,
"def": 40,
"xyz": 50
},
{
"yyj": 1,
"def": 40,
"xyz": 50
},
{
"yyj": 1,
"def": 40,
"xyz": 50
}
]
*/
#3
5
Here's another take on the OP's suggestion to use map()
for clarity (not performance).
这是对OP的建议的另一种看法,即使用map()来表示清晰度(而非性能)。
var newItems = items.map(item => ({
name: item.title,
id: item.uid
}));
This uses ES6 arrow functions and the shortcut syntaxes that are possible when there's only one parm passed to the function and only one statement in the body of the function.
这使用ES6箭头函数和当只有一个parm传递给函数并且函数体中只有一个语句时可能的快捷语法。
Depending on your history with lambda expressions in various languages, this form may or may not resonate with you.
根据您使用各种语言的lambda表达式的历史记录,此表单可能会或可能不会与您产生共鸣。
Be careful when returning an object literal in the arrow function shortcut syntax like this. Don't forget the additional parens around the object literal!
使用箭头函数快捷方式语法返回对象文字时要小心。不要忘记对象字面周围的额外parens!
#4
2
If you want to make it a little more reusable. Maybe this is a decent approach.
如果你想让它更可重复使用。也许这是一个不错的方法。
function rekey(arr, lookup) {
for (var i = 0; i < arr.length; i++) {
var obj = arr[i];
for (var fromKey in lookup) {
var toKey = lookup[fromKey];
var value = obj[fromKey];
if (value) {
obj[toKey] = value;
delete obj[fromKey];
}
}
}
return arr;
}
var arr = [{ apple: 'bar' }, { apple: 'foo' }];
var converted = rekey(arr, { apple: 'kung' });
console.log(converted);
#5
1
var jsonObj = [/*sample array in question*/ ]
Based on different benchmarks discussed below, fastest solution is native for:
根据下面讨论的不同基准,最快的解决方案原生于:
var arr = [];
for(var i = 0, len = jsonObj .length; i < len; i++) {
arr.push( {"name": jsonObj[i].title, "id" : jsonObj[i].uid});
}
I think alternatively without using a frameworks this will be option 2:
我认为,如果不使用框架,这将是选项2:
var arr = []
jsonObj.forEach(function(item) { arr.push({"name": item.title, "id" : item.uid }); });
There is always debate between using navite and non-navite functions. If I remember correctly lodash argued they were faster than underscore because the use non-native functions for key operations.
使用导航功能和非导航功能之间始终存在争议。如果我没记错的话,lodash认为他们比下划线更快,因为使用非原生函数进行关键操作。
However different browsers will produce sometimes very different results. I always looked for the best average.
但是,不同的浏览器有时会产生非常不同的结我总是寻找最好的平均值。
For benchmarks you can take a look at this:
对于基准测试,您可以看一下:
http://jsperf.com/lo-dash-v1-1-1-vs-underscore-v1-4-4/8
http://jsperf.com/lo-dash-v1-1-1-vs-underscore-v1-4-4/8
#6
1
Using ES6:
使用ES6:
const renameFieldInArrayOfObjects = (arr, oldField, newField) => {
return arr.map(s => {
return Object.keys(s).reduce((prev, next) => {
if(next === oldField) {
prev[newField] = s[next]
} else {
prev[next] = s[next]
}
return prev
}, {})
})
}
Using ES7:
使用ES7:
const renameFieldInArrayOfObjects = (arr, oldField, newField) => {
return arr.map(s => {
return Object.keys(s).reduce((prev, next) => {
return next === oldField
? {...prev, [newField]: s[next]}
: {...prev, [next]: s[next]}
}, {})
})
}
#7
1
You can use an npm package named node-data-transform.
您可以使用名为node-data-transform的npm包。
Your data :
你的数据:
const data = [
{
title: 'pineapple',
uid: 'ab982d34c98f',
},
{
title: 'carrots',
uid: '6f12e6ba45ec',
},
];
Your mapping :
你的映射:
const map = {
item: {
name: 'title',
id: 'uid',
},
};
And use the package :
并使用包:
const DataTransform = require("node-json-transform").DataTransform;
const dataTransform = DataTransform(data, map);
const result = dataTransform.transform();
console.log(result);
Result :
结果:
[
{
name: 'pineapple',
id: 'ab982d34c98f'
},
{
name: 'carrots',
id: '6f12e6ba45ec'
}
]
Maybe it's not the best way for performance, but it's quite elegant.
也许这不是性能的最佳方式,但它非常优雅。