2013-03-15 48 views
2

說我有以下對象數組:在一個對象數組中,我如何根據對象屬性進行聚合?

dataArray = [ 
    { id: "a", score: 1 }, 
    { id: "b", score: 2 }, 
    { id: "c", score: 5 }, 
    ... 
    { id: "a", score: 3 }, 
    ... 
    { id: "c", score: 2}, 
    ... 
] 

我怎樣才能獲得像resultArray如下:

resultArray = [ 
    { id: "a", score: sum of all the scores when id is a }, 
    { id: "b", score: sum of all the scores when id is b }, 
    ... 
    ... 
] 

回答

2

如果使用下劃線庫:

_.map _.groupBy(dataArray, 'id'), (v, k) -> 
    {id: k, score: _.reduce(v, ((m, i) -> m + i['score']), 0) } 
+1

是的,就是這樣。簡短而又甜美,但在迭代過程中多次迭代數據集並創建更大的數據集。 – 2013-03-15 21:46:34

+2

經典的時間/空間/大腦權衡。 – 2013-03-15 22:00:40

0

這只是普通的JavaScript,但這裏是長回答你的問題:

function aggregate(values, init, keyGetter, valueGetter, aggregator) { 
     var results = {} 
     for (var index = 0; index != values.length; ++index) { 
      var value = values[index] 
      var key = keyGetter(value) 
      var soFar; 
      if (key in results) { 
       soFar = results[key] 
      } else { 
       soFar = init 
      } 
      value = valueGetter(value) 
      results[key] = aggregator(soFar, value) 
     } 
     return results 
    } 

    var array = [ 
     { id: 'a', score: 1 }, 
     { id: 'b', score: 2 }, 
     { id: 'c', score: 5 }, 
     { id: 'a', score: 3 }, 
     { id: 'c', score: 2 } 
    ] 

    function keyGetter(value) { 
     return value.id 
    } 

    function valueGetter(value) { 
     return value.score 
    } 

    function aggregator(sum, value) { 
     return sum + value 
    } 

    function ready() { 
     var results = aggregate(array, 0, keyGetter, valueGetter, aggregator) 
     console.info(results) 
    } 
+0

老實說,這看起來更像是java而不是javascript。我很確定一個慣用的javascript版本會更簡潔。 – 2013-03-15 21:23:32

+0

這是日常的JavaScript。 getter函數和'init'值可以被內聯。類似underscore.js的庫在內部看起來像這樣,使用高階函數保持通用。 – 2013-03-15 23:52:29

0

這裏是一個簡單的CoffeeScript版本:

data = [ 
    { id: "a", score: 1 } 
    { id: "b", score: 2 } 
    { id: "a", score: 5 } 
    { id: "c", score: 2 } 
    { id: "b", score: 3 } 
] 

# Aggregate scores in a map. 
resultSet = {} 
for obj in data 
    resultSet[obj.id] ?= 0 
    resultSet[obj.id] += obj.score 
console.log resultSet 

# Create array from map. 
resultArr = for key, val of resultSet 
    { id: key, score: val} 
console.log resultArr 

輸出是:

{ a: 6, b: 5, c: 2 } 
[ { id: 'a', score: 6 }, 
    { id: 'b', score: 5 }, 
    { id: 'c', score: 2 } ] 

我敢肯定,這可以創建使用下劃線功能的發燒友的解決方案,但CoffeeScript的解決方案是不壞,所以我去了一些簡單的瞭解。

1

下劃線的版本可能是最簡潔的。這是一個純CoffeeScript的版本,只創建一個輔助對象有通過ID快速訪問,使整個事情O(N):

aggregateScores = (dataArr) -> 
    scores = {} 
    for {id, score} in dataArr 
    scores[id] = (scores[id] or 0) + score 
    {id, score} for id, score of scores 

console.log aggregateScores [ 
    { id: "a", score: 1 } 
    { id: "b", score: 2 } 
    { id: "c", score: 5 } 
    { id: "a", score: 3 } 
    { id: "c", score: 2 } 
] 
# Output: 
# [{id:"a", score:4}, {id:"b", score:2}, {id:"c", score:7}] 
0

這是一個有點矯枉過正,如果這是你想要做的唯一的聚集,但有一個名爲Lumenize的良好記錄的聚合庫,除了更高級的數據透視表,n維多維數據集,分層累積和時區精確的時間序列聚合之外,它還可以執行這種簡單的分組操作。

Here is the jsFiddle Lumenize解決方案。

如果你想嘗試它的Node.js:

npm install Lumenize --save 

然後把這個到名爲lumenizeGroupBy.coffee文件:

lumenize = require('Lumenize') 
dataArray = [ 
    { id: "a", score: 1 }, 
    { id: "b", score: 2 }, 
    { id: "c", score: 5 }, 
    { id: "a", score: 3 }, 
    { id: "c", score: 2} 
] 

dimensions = [{field:'id'}] 
metrics = [{field: 'score', f: 'sum', as: 'sum'}] 
config = {dimensions, metrics} 

cube = new lumenize.OLAPCube(config, dataArray) 
console.log(cube.toString(null, null, 'sum')) 

和運行

coffee lumenizeGroupBy.coffee 
相關問題