2017-06-22 130 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/