2017-06-22 26 views
0

によってのは、私はJavaScriptで次のようなデータ構造を持っているとしましょう:Javascriptの計算割合の合計キー

const data = [ 
     {year: 2017, kind: "mammal", animal: "dog", total: 5}, 
     {year: 2017, kind: "mammal", animal: "dog", total: 3}, 
     {year: 2016, kind: "mammal", animal: "dog", total: 5}, 
     {year: 2016, kind: "bird", animal: "chicken", total: 5}, 
     {year: 2016, kind: "bird", animal: "chicken", total: 90} 
]; 

私は、このようなグループにyearなどのキーまたは複数のキーを指定できるようにしたいと思いますデータ。だから、僕は次のように私は合計を計算したいと思うことで、グループとしてyearを指定した場合:

[{year: 2017, kind: "mammal", animal: "dog", count: 8, pct: 1}, 
    {year: 2016, kind: "mammal", animal: "dog", count: 5, pct: 0.05}, 
    {year: 2016, kind: "bird", animal: "chicken", count: 95, pct: 0.95}] 

をまたは私はyearkindでグループ合計を指定した場合、私は次のよう望む:

[{year: 2017, kind: "mammal", animal: "dog", count: 8, pct: 1}, 
    {year: 2016, kind: "mammal", animal: "dog", count: 5, pct: 1}, 
    {year: 2016, kind: "bird", animal: "chicken", count: 95, pct: 1}] 

方法をこれをJavascriptで実装できますか?私はd3のネスト関数を使用して見てきましたが、これまでのところ実装に頭を落としていませんでした。

+2

これまでに何を試しましたか? – Luca

+1

'sort'と' reduce'メソッドを見てください。 [配列のドキュメント](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) – Will

答えて

1

私はあなたがそれを実装しようとする前に、あなたの要求をはっきりと理解する必要があると思います。あなたの説明から、私の理解は結果のために年+種類+動物が結果項目の鍵です。また、指定したグループはパーセンテージの計算にのみ使用されます。

まずグループ年+種類+

function groupItems(data){ 
    var resultMap = {}; 
    for(var index in data){ 
     var item = data[index]; 
     var key = item['year']+'###'+item['kind']+'###'+item['animal']; 
     if(!resultMap[key]){ 
     //copy the item to a new object to make sure it does not update the origin data 
     resultMap[key] = Object.assign({}, item); 
     }else{ 
     // sum them up 
     resultMap[key]['total'] = resultMap[key]['total'] +item['total']; 
     } 
    } 
    return Object.values(resultMap); 
} 

は、今、私たちはアイテムを持っている動物に基づいてアイテムは、何が必要指定したグループに基づいて割合を計算することです。

function computePercentage(groupedItems, withGroups){ 
    //compute the total items per specified group 
    var totalItemsPerGroup = {}; 
    for(var index in groupedItems){ 
     var item = groupedItems[index]; 
     var keyValues = getValuesFromObjectWithKeys(item, withGroups); 
     var strKey = keyValues.join('###'); 
     if(totalItemsPerGroup[strKey] != undefined){ 
      totalItemsPerGroup[strKey] += item['total']; 
     }else{ 
      totalItemsPerGroup[strKey] = item['total']; 
     } 
    } 
    // compute percentage based on the total items per specified group calculated 
    var result = []; 
    for(var index in groupedItems){ 
     var item = groupedItems[index]; 
     var keyValues = getValuesFromObjectWithKeys(item, withGroups); 
     var strKey = keyValues.join('###'); 
     var totalCount = totalItemsPerGroup[strKey]; 
     var percentage = totalCount == 0 ? 0 : item['total']/totalCount; 
     var resultItem = {}; 
     resultItem['year'] = item['year']; 
     resultItem['kind'] = item['kind']; 
     resultItem['animal'] = item['animal']; 
     resultItem['count'] = item['total']; 
     resultItem['pct'] = percentage; 
     result.push(resultItem); 
    } 
    return result; 
} 

// a helper function to get values based on specified keys 
function getValuesFromObjectWithKeys(obj, keys){ 
    var result = []; 
    for(var i in keys){ 
     if(obj[keys[i]]){ 
      result.push(obj[keys[i]]); 
     } 
    } 
    return result; 
} 

ので、これらの機能を使用するには:

var grouppedItems = groupItems(data); 
var yearPercentage = computePercentage(grouppedItems , ['year']); 
console.log(JSON.stringify(yearPercentage)); 
var yearKindPercentage = computePercentage(grouppedItems , ['year', 'kind']); 
console.log(JSON.stringify(yearKindPercentage)); 
var kindAnimalPercentage = computePercentage(grouppedItems , ['kind', 'animal']); 
console.log(JSON.stringify(kindAnimalPercentage)); 
1

少なくともために(私はあなたがlodash-fpramdaを使用している場合は特に、これを行うにはクリーンな方法があると確信しているが、これは動作します最初の結果セット):

const computeBy = (primaryKey, secondary, data) => { 

    const groupBy = (key, data) => data.reduce((acc, record) => { 
    const value = record[key]; 
    acc[value] = acc[value] || []; 
    acc[value].push(record); 
    return acc; 
    }, {}); 

    const groupedByPrimary = groupBy(primaryKey, data); 

    const sum = records => records.reduce((acc, v) => acc + v.total, 0); 

    return Object.keys(groupedByPrimary).reduce((acc, key) => { 
    const group = groupedByPrimary[key]; 
    const groupTotalCount = sum(group); 
    const groupedBySecondary = groupBy(secondary, group); 
    const final = Object.values(groupedBySecondary).map(secondaryGroup => { 
     const count = sum(secondaryGroup); 
     const pct = count/groupTotalCount; 
     const recordWithPercentage = Object.assign({}, secondaryGroup[0], { pct, count }); 
     delete recordWithPercentage.total; 
     return recordWithPercentage; 
    }); 
    return acc.concat(final); 
    }, []); 
} 

const result = computeBy('year', 'kind', data).sort((a, b) => a.year < b.year); 

ワーキングフィドル:https://jsfiddle.net/frb6bowj/2/