2012-09-06 272 views
4

从服务中,我收到一个带有键值对的JSON对象,我需要从它们动态地创建对象,按一列分组。从键值对创建对象数组

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

我需要将其转换到以下对象数组:

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

每个组可以包含任意数量的键 - 值对。

目前我对这个工作的解决方案,但我不知道这是否是最佳的:

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

您可以检查这里的小提琴:http://jsbin.com/ozizom/1/

你对此有一个更好的解决方案?

+0

按照你的例子,JSON是否总是按Group排序? – sp00m

+0

是的,JSON字段组,键和值给出,并且记录总是按组排序 – tpolyak

回答

5

假设JSON记录将始终按组进行排序,这里是另一种方法:

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是一个工作的例子。

+0

谢谢,它比我的快50%:http://jsperf.com/javascript-key-value – tpolyak

+0

我没有知道jsperf.com,这真棒!感谢你;) – sp00m

+0

虽然发现了一个问题。只有当群组按顺序到达时(AAABBBCCC),您的解决方案才能正常工作。例如如果订单是AAABBCCCB,则创建4条记录:http://jsfiddle.net/GrmZF/2/ – tpolyak

3

下面是一个解决方案,通过使用JavaScript成语缩减代码大小(对于更好或更糟:-)。该解决方案不依赖输入值的顺序:

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

工作示例是在http://jsfiddle.net/arQww

这里是最快的解决方案,我可以找到,其假定值将始终按组进行排序:

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

的性能对比是http://jsperf.com/vnmzc。虽然第二种解决方案更快,但两者的性能都是O(n),而它们之间的现实差异将是无关紧要的,所以第一种解决方案可能更好,因为它更简单,更通用。

-1

我需要类似问题的帮助,购买我想总结价值。我有这个数组与对象

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

我希望用户选择从一个选项菜单中总结的月份。

因此,如果用户从选择菜单,我要总结的所有ValueOneToSum值,并根据Aug2014ValueTwoToSum值,我怎么能做到这一点选择August 2014

例如:totalSumOneAug2014168totalSumTwo55

1

如果你不得不操纵数据,我会推荐underscore框架。 这就是解决方案的外观:

/* 
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'))) 
    })