简体   繁体   English

从键值对创建对象数组

[英]Creating object array from key-value pairs

From a service I receive a JSON object with key-value pairs, and I need to dynamically create objects from them, grouped by one column. 从服务我收到一个带键值对的JSON对象,我需要从它们动态创建对象,按一列分组。

The JSON looks like this: JSON看起来像这样:

[
    { "Group" : "A", "Key" : "Name", "Value" : "John" },
    { "Group" : "A", "Key" : "Age",  "Value" : "30" },
    { "Group" : "A", "Key" : "City", "Value" : "London" },
    { "Group" : "B", "Key" : "Name", "Value" : "Hans" },
    { "Group" : "B", "Key" : "Age",  "Value" : "35" },
    { "Group" : "B", "Key" : "City", "Value" : "Berlin" },
    { "Group" : "C", "Key" : "Name", "Value" : "José" },
    { "Group" : "C", "Key" : "Age",  "Value" : "25" },
    { "Group" : "C", "Key" : "City", "Value" : "Madrid" }
]

I would need to transform it to the following array of objects: 我需要将其转换为以下对象数组:

[
    { Group : "A", Name : "John", Age : 30, City : "London" },
    { Group : "B", Name : "Hans", Age : 35, City : "Berlin" },
    { Group : "C", Name : "José", Age : 25, City : "Madrid" }
]

Each group can contain any number of key-value pairs. 每个组可以包含任意数量的键值对。

Currently I have a working solution for this, but I don't know if it's optimal: 目前我有一个可行的解决方案,但我不知道它是否是最佳的:

var items = [
    { "Group" : "A", "Key" : "Name", "Value" : "John" },
    { "Group" : "A", "Key" : "Age",  "Value" : "30" },
    { "Group" : "A", "Key" : "City", "Value" : "London" },
    { "Group" : "B", "Key" : "Name", "Value" : "Hans" },
    { "Group" : "B", "Key" : "Age",  "Value" : "35" },
    { "Group" : "B", "Key" : "City", "Value" : "Berlin" },
    { "Group" : "C", "Key" : "Name", "Value" : "José" },
    { "Group" : "C", "Key" : "Age",  "Value" : "25" },
    { "Group" : "C", "Key" : "City", "Value" : "Madrid" }
], item, record, hash = {}, results = [];

// Create a "hash" object to build up
for (var i = 0, len = items.length; i < len; i += 1) {
    item = items[i];

  if (!hash[item.Group]) {
    hash[item.Group] = {
      Group : item.Group
    };
  }
  hash[item.Group][item.Key] = item.Value;
}

// Push each item in the hash to the array
for (record in hash) {
  if(hash.hasOwnProperty(record)) {
    results.push(hash[record]);
  }
}

You can check the fiddle here: http://jsbin.com/ozizom/1/ 你可以在这里查看小提琴: http//jsbin.com/ozizom/1/

Do you have a better solution for this? 你有更好的解决方案吗?

Assuming that the JSON records will always be sorted by Group, here is another approach: 假设JSON记录将始终按Group排序,这是另一种方法:

var json = [
    { "Group" : "A", "Key" : "Name", "Value" : "John" },
    { "Group" : "A", "Key" : "Age",  "Value" : "30" },
    { "Group" : "A", "Key" : "City", "Value" : "London" },
    { "Group" : "B", "Key" : "Name", "Value" : "Hans" },
    { "Group" : "B", "Key" : "Age",  "Value" : "35" },
    { "Group" : "B", "Key" : "City", "Value" : "Berlin" },
    { "Group" : "C", "Key" : "Name", "Value" : "José" },
    { "Group" : "C", "Key" : "Age",  "Value" : "25" },
    { "Group" : "C", "Key" : "City", "Value" : "Madrid" }
];

var array = [];
var previousGroup = null;

for(var i=0; i<json.length; i++) {
    var group = json[i].Group;
    if(previousGroup != group) {
        array.push({Group: group});
        previousGroup = group;
    }
    array[array.length-1][json[i].Key] = json[i].Value;
}

Here is a working example. 是一个有效的例子。

Here's a solution where the code size is reduced (for better or for worse :-) by the use of JavaScript idioms. 这是一个解决方案,通过使用JavaScript惯用法减少代码大小(更好或更坏:-)。 This solution doesn't depend on the order of input values: 此解决方案不依赖于输入值的顺序:

var values = [
  {Group: 'A', Key: 'Name', Value: 'John'},
  {Group: 'A', Key: 'Age', Value: '30'},
  {Group: 'A', Key: 'City', Value: 'London'},
  {Group: 'B', Key: 'Name', Value: 'Hans'},
  {Group: 'B', Key: 'Age', Value: '35'},
  {Group: 'B', Key: 'City', Value: 'Berlin'},
  {Group: 'C', Key: 'Name', Value: 'José'},
  {Group: 'C', Key: 'Age', Value: '25'},
  {Group: 'C', Key: 'City', Value: 'Madrid'}
];

var map = {};
values.forEach(function(value) {
  map[value.Group] = map[value.Group] || {Group: value.Group};
  map[value.Group][value.Key] = value.Value;
});

var results = Object.keys(map).map(function(key) { return map[key]; });

A working example is at http://jsfiddle.net/arQww . 一个工作示例位于http://jsfiddle.net/arQww

Here's the fastest solution I can find, which assumes that the values will always be sorted by Group: 这是我能找到的最快的解决方案,它假定值总是按组排序:

var group, results = [];
for (var i = 0; i < values.length; ) {
  results.push({Group: group = values[i].Group});
  do {
    results.push[results.length - 1][values[i].Key] = values[i].Value;  
  } while (++i < values.length && values[i].Group == group);
}

A performance comparison is at http://jsperf.com/vnmzc . 性能比较见http://jsperf.com/vnmzc While this second solutions is faster, the performance of both is O(n) and the real-world difference between them will be inconsequential, so the first solution is probably preferable since it's simpler and more general. 虽然第二种解决方案更快,但两者的性能都是O(n),它们之间的实际差异将是无关紧要的,因此第一种解决方案可能更为可取,因为它更简单,更通用。

If you have to manipulate data a lot I would recommend underscore framework. 如果你必须经常操作数据,我会建议使用下划线框架。 This is how solution will look in it: 这就是解决方案的内容:

/*
 We group items into object that looks like {group: attributes, ..}
 Then for each group we create result object denoting group,
 and extend result with object created from keys and values of attributes
*/
_.map(_.groupBy(items, function (item) {return item.Group}),
     function (attributes, group) {
        return _.extend({Group: group},
                        _.object(_.pluck(attributes, 'Key'),
                                 _.pluck(attributes, 'Value')))
    })

I need help with a similar problem, buy I want to sum values. 我需要一个类似问题的帮助,买我想要总结价值。 I have this array with objects 我有这个带有对象的数组

var myArrWithObj = [
    {DateToSort: "Jul2014", ValueOneToSum: "60", ValueTwoToSum: "15"},
    {DateToSort: "Jul2014", ValueOneToSum: "30", ValueTwoToSum: "50"},
    {DateToSort: "Jul2014", ValueOneToSum: "12", ValueTwoToSum: "22"},
    {DateToSort: "Aug2014", ValueOneToSum: "65", ValueTwoToSum: "25"},
    {DateToSort: "Aug2014", ValueOneToSum: "13", ValueTwoToSum: "10"},
    {DateToSort: "Aug2014", ValueOneToSum: "90", ValueTwoToSum: "20"},
    {DateToSort: "Sep2014", ValueOneToSum: "60", ValueTwoToSum: "15"},
    {DateToSort: "Sep2014", ValueOneToSum: "60", ValueTwoToSum: "18"},
    {DateToSort: "Sep2014", ValueOneToSum: "75", ValueTwoToSum: "18"}
                   ];

and I want the user to choose what month to sum from an option menu. 我希望用户从选项菜单中选择要汇总的月份。

So If the user selects August 2014 from the select menu, I want to sum all the ValueOneToSum values and ValueTwoToSum values based on Aug2014 , how could I do that? 因此,如果用户选择August 2014从选择菜单,我要总结的所有ValueOneToSum值和ValueTwoToSum基于价值观Aug2014 ,我怎么能做到这一点?

For example: the totalSumOne for Aug2014 would be 168 and totalSumTwo would be 55 . 例如: totalSumOneAug2014168totalSumTwo55

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

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