简体   繁体   English

有效地重命名/重新映射对象数组中的 javascript/json 对象键

[英]Efficiently rename/re-map javascript/json object keys within array of objects

I have some structured JSON data like so.我有一些像这样的结构化 JSON 数据。 Let's assume this is interchangeable, via JSON.parse() :让我们假设这是可互换的,通过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:我需要它看起来像这样,将title重新映射到name ,将uid重新映射到id并得到结果:

[
    {
        "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?这一切都很好,但如果数组中有 200,000 个对象呢? This is a lot of processing overhead.这是大量的处理开销。

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.如果您的方法更有效,请提供证明/参考。

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.如果您无论如何都必须解析 JSON,则更结构化的方法是reviver函数传递给JSON.parse ,您可能能够避免对数组进行额外的传递。 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.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:测试脚本,加载 100,230 行 OP 的示例 JSON:

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

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.很久以前问过这个问题,从那时起,我已经习惯于使用Array.prototype.map()来完成工作,更多的是为了代码的稳定性和清洁度而不是性能。 While it's certainly not the most performant, it looks great:虽然它肯定不是性能最好的,但它看起来很棒:

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)))

eg例如

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
    }
]
*/

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 箭头函数和快捷语法,当只有一个参数传递给函数并且函数体中只有一个语句时,这些语法是可能的。

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!不要忘记对象文字周围的附加括号!

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

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]}
    }, {})
  })
}
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.在使用 Navite 和非 Navite 函数之间总是存在争论。 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

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.也许这不是性能的最佳方式,但它非常优雅。

function replaceElem(value, replace, str) {
            while (str.indexOf(value) > -1) {
                str = str.replace(value, replace);
            }
            return str;
        }

call this from main从主调用这个

var value = "tittle";
var replace = "name";
replaceElem(value, replace, str);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM