2012-07-27 142 views
1

我试图让我的头绕着Javascript数组函数。我有一个嵌套的数组像这样,在每一行覆盖相同时间段:Javascript嵌套数组转换

[{ 
    "category": "fruit", 
    "variety": "apple", 
    "data": [{ 
     "day": 1, 
     "units": 2 
    }, {"day": 2, 
     "units": 4 
    }] 
},{ 
    "category": "fruit", 
    "variety": "orange", 
    "data": [{ 
     "day": 1, 
     "units": 3 
    }, {"day": 2, 
     "units": 5 
    }] 
},{ 
    "category": "veg", 
    "variety": "tomato", 
    "data": [{ 
     "day": 1, 
     "units": 4 
    }, {"day": 2, 
     "units": 2 
    }] 
}] 

我想白天按类别进行总结的单位,要得到这样的数组:

[{ 
    "category": "fruit", 
    "data": [{ 
     "day": 1, 
     "units": 5 
    }, {"day": 2, 
     "units": 9 
    }] 
},{ 
    "category": "veg", 
    "data": [{ 
     "day": 1, 
     "units": 4 
    }, {"day": 2, 
     "units": 2 
    }] 
}] 

我一直在通过长循环if语句来解决这个问题,并对它做了一些散列。你能看到一个优雅的方式来解决这个问题吗?

非常感谢!

回答

2

解决方案非常明显:循环访问数组,并将数据存储在键值对中。然后,循环访问has,并使用Array.prototype.map构造得到的数组。最后,如果您想要一个格式良好的JSON字符串,请使用JSON.stringify(result, null, 4);,其中4是相当格式化的间隔数。

演示:http://jsfiddle.net/jde6S/

var list = [ ... ]; 
var hash = {}; 
for (var i=0; i<list.length; i++) { 
    var obj = list[i]; 

    // This part makes sure that hash looks like {fruit:[], veg: []} 
    var hashObjCat = hash[obj.category]; 
    if (!hashObjCat) { 
     hashObjCat = hash[obj.category] = {}; 
    } 

    // This part populates the hash hashObjCat with day-unit pairs 
    for (var j=0; j<obj.data.length; j++) { 
     var data = obj.data[j]; 
     if (hashObjCat[data.day]) hashObjCat[data.day] += data.units; 
     else hashObjCat[data.day] = data.units; 
    } 
} 
// Now, we hash looks like {fruit: {1:5, 2:9} } 
// Construct desired object 
var result = Object.keys(hash).map(function(category) { 
    // Initial object 
    var obj = {category: category, data:[]}; 
    var dayData = Object.keys(hash[category]); 
    // This part adds day+units dicts to the data array 
    for (var i=0; i<dayData.length; i++) { 
     var day = dayData[i]; 
     var units = hash[category][day]; 
     obj.data.push({day: day, units: units}); 
    } 
    return obj; 
}); 
// Test: 
console.log(JSON.stringify(result, null, 4)); 
+0

尼斯:你用循环做什么,我用'红眼因为;以及我在哪里使用循环,你可以在'Object.keys'上使用'map' :-) – Bergi 2012-07-27 20:51:47

+0

我特意使用'for'循环,因为如果你不熟悉JavaScript,那么更容易移植到其他语言。我的答案的目标是可读的代码。我解开了你的编辑,因为我使用中间变量来显示逻辑步骤(和真实语言一样,我更喜欢混合多种方法,使用多个Object.keys(..)。maps'看起来有点重复:p)。 – 2012-07-27 20:57:34

+0

好的,但我会说代码应该只使用一种方法,即使是重复性的(等待,可能是一个非常高级的函数在这里帮助?)。如果你曾经理解过map()是如何工作的,那么这个部分根本不需要额外的解释:-) – Bergi 2012-07-27 21:40:31

0

如果您愿意获取一些外部代码并使用它来实质性地重新索引您的结构,那么您可能会做一些事情。我知道旧的dojo数据API是一团糟的工作,但可能允许像你似乎要求的东西。

就我个人而言,我会坚持循环,只是保持您的变量名称可读。还要记住,对象文字可以作为数组/哈希语法x [y]或点语法x.y

2

reduce数组对象(见@ RobW的答案如何做到这一点与循环):

var data = [...] // your input 

// Iterate the data with reduce... 
var sumsbycategory = data.reduce(function(map, fruit) { 
    var cat = fruit.category; 
    // set an property to an object, iterating the days array... 
    map[cat] = fruit.data.reduce(function(sums, day) { 
     var d = day.day; 
     // set or update the units for this day 
     sums[d] = (sums[d] || 0) + day.units; 
     return sums; // into the next iteration 
    }, map[cat] || {}) // ...passing in the already existing map for this cat or a new one 
    return map; // into the next iteration 
}, {}); // ...passing in an empty object 

现在,我们有以下格式:

{"fruit":{"1":5,"2":9},"veg":{"1":4,"2":2}} 

......我认为这样更容易处理,但让我们建立y我们的阵列:

var result = []; // init array 
for (var cat in sumsbycategory) { // loop over categories 
    var data = []; // init array 
    // add category object: 
    result.push({category:cat, data:data}); 
    for (var day in sumsbycategory[cat]) // loop over days in category 
     // add day object 
     data.push({day:day, units:sumsbycategory[cat][day]}); 
} 

但是,等等!一个对象没有顺序,并且可能发生day2在结果数组中出现days1之前(这可能会破坏您的应用程序?)因此,您可以在该对象的keys上使用10,该对象也可以是sorted,以生成数组在一个干净的前瞻性表达:

var result = Object.keys(sumsbycategory).map(function(cat) { 
    return { 
     category: cat, 
     data: Object.keys(sumsbycategory[cat]) 
     .sort(function numbercompare(a,b){ return a-b; }) 
     .map(function(day) { 
      return { 
       day: day, 
       units: sumsbycategory[cat][day] 
      }; 
     }) 
    }; 
}); 

结果:

[{ 
    "category": "fruit", 
    "data": [{"day":"1","units":5},{"day":"2","units":9}] 
},{ 
    "category": "veg", 
    "data": [{"day":"1","units":4},{"day":"2","units":2}] 
}] 

Demo at jsfiddle.net

+0

您能否通过向代码添加注释来解释各个部分的作用?这会使它更易读,更容易维护。 – 2012-07-27 21:06:25

+0

我认为这样简洁的简明代码的最佳文档将是英文解释它对整个函数(OPs问题中给出的文本)以及JSON示例的作用。地图函数imho根本不需要任何注释,它只会重复非常富有表现力的代码。 – Bergi 2012-07-27 21:37:29

+0

谢谢。这非常有帮助。在map,reduce和Object.keys上没有更好的教程。你和Rob之间的讨论也有助于理解不同的方法。我对接受哪个答案感到厌烦,但我想我必须根据jsfiddle演示的额外接触将其交给Rob。再次感谢! – 2012-07-28 07:10:19