使用reduce

时间:2018-06-26 10:29:07

标签: javascript group-by

即使我强烈认为它必须存在,我也很难在其他stackoverflow帖子中找到所需的解决方案。如果有,请按照正确的方向转发我。

我正在尝试使用体育数据在javascript中进行标准分组。我有以下对象数组:

 const myData = [
    {team: "GSW", pts: 120, ast: 18, reb: 11},
    {team: "GSW", pts: 125, ast: 28, reb: 18},
    {team: "GSW", pts: 110, ast: 35, reb: 47},
    {team: "HOU", pts: 100, ast: 17, reb: 43},
    {team: "HOU", pts: 102, ast: 14, reb: 32},
    {team: "SAS", pts: 127, ast: 21, reb: 25},
    {team: "SAS", pts: 135, ast: 25, reb: 37},
    {team: "SAS", pts: 142, ast: 18, reb: 27}
 ]

我的数据中的每一行都对应于特定篮球比赛的结果。简而言之,我想对数据进行分组,并对分组后的数据应用均值/平均值函数。我期望的结果是:

const groupedData = [
    {team: "GSW", pts: 118.3, ast: 27.0, reb: 25.3},
    {team: "HOU", pts: 101, ast: 15.5, reb: 37.5},
    {team: "SAS", pts: 134.7, ast: 21.3, reb: 29.7} 
] 

我更喜欢在此处使用带有reduce()的原始javascript ...鉴于我对reduce的了解,这似乎是最好的方法。我目前正在研究此问题,如果有人可以在别人发布答案之前将其投入使用,则会发布。

编辑:我的实际数据有30个键。我希望找到一个解决方案,简单地要求我要么(a)仅指定要分组的“团队”列,然后假设将其余的分组,要么(b)传递一组统计列(点,资产等)。 ),而不是为每个统计信息创建一行。

谢谢!

7 个答案:

答案 0 :(得分:5)

一种方法是结合使用reducemap

const myData = [
    {team: "GSW", pts: 120, ast: 18, reb: 11},
    {team: "GSW", pts: 125, ast: 28, reb: 18},
    {team: "GSW", pts: 110, ast: 35, reb: 47},
    {team: "HOU", pts: 100, ast: 17, reb: 43},
    {team: "HOU", pts: 102, ast: 14, reb: 32},
    {team: "SAS", pts: 127, ast: 21, reb: 25},
    {team: "SAS", pts: 135, ast: 25, reb: 37},
    {team: "SAS", pts: 142, ast: 18, reb: 27}
 ]
 
 // Calculate the sums and group data (while tracking count)
 const reduced = myData.reduce(function(m, d){
    if(!m[d.team]){
      m[d.team] = {...d, count: 1};
      return m;
    }
    m[d.team].pts += d.pts;
    m[d.team].ast += d.ast;
    m[d.team].reb += d.reb;
    m[d.team].count += 1;
    return m;
 },{});
 
 // Create new array from grouped data and compute the average
 const result = Object.keys(reduced).map(function(k){
     const item  = reduced[k];
     return {
         team: item.team,
         ast: item.ast/item.count,
         pts: item.pts/item.count,
         reb: item.reb/item.count
     }
 })
 
 console.log(JSON.stringify(result,null,4));

编辑:刚刚看到您对问题的更新。如果可以将白名单(提供要计算的键数组)或黑名单(提供要忽略的键数组)键以编程方式做到这一点,则可以取消每个键的每一行。

const myData = [
    {team: "GSW", pts: 120, ast: 18, reb: 11},
    {team: "GSW", pts: 125, ast: 28, reb: 18},
    {team: "GSW", pts: 110, ast: 35, reb: 47},
    {team: "HOU", pts: 100, ast: 17, reb: 43},
    {team: "HOU", pts: 102, ast: 14, reb: 32},
    {team: "SAS", pts: 127, ast: 21, reb: 25},
    {team: "SAS", pts: 135, ast: 25, reb: 37},
    {team: "SAS", pts: 142, ast: 18, reb: 27}
 ]
 
/**
 * Function which accepts a data array and a list of whitelisted
 * keys to find the average of each key after grouping
 */
function getGroupedData(data, whitelist) {
  // Calculate the sums and group data (while tracking count)
  const reduced = data.reduce(function(m, d) {
    if (!m[d.team]) {
      m[d.team] = { ...d,
        count: 1
      };
      return m;
    }
    whitelist.forEach(function(key) {
      m[d.team][key] += d[key];
    });
    m[d.team].count += 1;
    return m;
  }, {});

  // Create new array from grouped data and compute the average
  return Object.keys(reduced).map(function(k) {
    const item = reduced[k];
    const itemAverage = whitelist.reduce(function(m, key) {
      m[key] = item[key] / item.count;
      return m;
    }, {})
    return {
      ...item, // Preserve any non white-listed keys
      ...itemAverage // Add computed averege for whitelisted keys
    }
  })
}


console.log(JSON.stringify(getGroupedData(myData, ['pts', 'ast', 'reb']), null, 4));

答案 1 :(得分:0)

const myData = [
    {team: "GSW", pts: 120, ast: 18, reb: 11},
    {team: "GSW", pts: 125, ast: 28, reb: 18},
    {team: "GSW", pts: 110, ast: 35, reb: 47},
    {team: "HOU", pts: 100, ast: 17, reb: 43},
    {team: "HOU", pts: 102, ast: 14, reb: 32},
    {team: "SAS", pts: 127, ast: 21, reb: 25},
    {team: "SAS", pts: 135, ast: 25, reb: 37},
    {team: "SAS", pts: 142, ast: 18, reb: 27}
 ]

const groubElement = myData.reduce((obj, val) => {
    if (obj[val.team]) {
        obj[val.team].pts = obj[val.team].pts + val.pts;
        obj[val.team].ast = obj[val.team].pts + val.ast;
        obj[val.team].reb = obj[val.team].pts + val.reb;
        obj[val.team].counter = obj[val.team].counter + 1;
    } else {
        obj[val.team] = val;
        obj[val.team].counter = 1;
    }
    return obj;

}, {});



const groupElementWithMean = Object.values(groubElement).map(({
    counter,
    ...element
}) => {
    element.pts = (element.pts / counter).toFixed(1);
    element.ast = (element.ast / counter).toFixed(1);
    element.reb = (element.reb / counter).toFixed(1);
    return element;
});

console.log(groupElementWithMean);

答案 2 :(得分:0)

您可以通过将reduceObject.keysArray.prototype.map结合使用,如下所示:-

const myData = [
    { team: "GSW", pts: 120, ast: 18, reb: 11 },
    { team: "GSW", pts: 125, ast: 28, reb: 18 },
    { team: "GSW", pts: 110, ast: 35, reb: 47 },
    { team: "HOU", pts: 100, ast: 17, reb: 43 },
    { team: "HOU", pts: 102, ast: 14, reb: 32 },
    { team: "SAS", pts: 127, ast: 21, reb: 25 },
    { team: "SAS", pts: 135, ast: 25, reb: 37 },
    { team: "SAS", pts: 142, ast: 18, reb: 27 }
]

let grpData = myData.reduce((acc, cv) => {
    if (!acc[cv.team]) {
        acc[cv.team] = {};
        acc[cv.team].team = cv.team;
        acc[cv.team].count = acc[cv.team].pts = acc[cv.team].ast = acc[cv.team].reb = 0
    }
    acc[cv.team].count++;
    acc[cv.team].pts += cv.pts;
    acc[cv.team].ast += cv.ast;
    acc[cv.team].reb += cv.reb;
    return acc;
}, {});
grpData = Object.keys(grpData).map(key => {
    let { team, reb, ast, pts, count } = grpData[key];
    return {
        team, reb: reb / count, ast: ast / count, pts: pts / count
    };
})
console.log(grpData);

答案 3 :(得分:0)

您可以使用Map采取动态方法,并在收集未知密钥后生成所有项目。

function groupBy(array, key) {
    return Array.from(
        array.reduce((m, o) => {
            var temp = m.get(o[key]);
            if (!temp) {
                m.set(o[key], temp = {});
            }
            Object.entries(o).forEach(([k, v]) => {
                if (k === key) {
                    return;
                }
                temp[k] = temp[k]  || { sum: 0, count: 0 };
                temp[k].sum += v;
                temp[k].count++;
            });
            return m;
        }, new Map),
        ([k, v]) => Object.assign({ [key]: k }, ...Object.entries(v).map(([l, { sum, count }]) => ({ [l]: +(sum / count).toFixed(1) })))
    );
}

const myData = [{ team: "GSW", pts: 120, ast: 18, reb: 11 }, { team: "GSW", pts: 125, ast: 28, reb: 18 }, { team: "GSW", pts: 110, ast: 35, reb: 47 }, { team: "HOU", pts: 100, ast: 17, reb: 43 }, { team: "HOU", pts: 102, ast: 14, reb: 32 }, { team: "SAS", pts: 127, ast: 21, reb: 25 }, { team: "SAS", pts: 135, ast: 25, reb: 37 }, { team: "SAS", pts: 142, ast: 18, reb: 27 }];

console.log(groupBy(myData, 'team'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

具有休息属性(babel: true)。

function groupBy(array, key) {
    return Array.from(
        array.reduce((m, { [key]: k, ...rest}) => {
            var temp = m.get(k);
            if (!temp) {
                m.set(k, temp = {});
            }
            Object.entries(rest).forEach(([l, v]) => {
                temp[l] = temp[l]  || { sum: 0, count: 0 };
                temp[l].sum += v;
                temp[l].count++;
            });
            return m;
        }, new Map),
        ([k, v]) => Object.assign({ [key]: k }, ...Object.entries(v).map(([l, { sum, count }]) => ({ [l]: +(sum / count).toFixed(1) })))
    );
}

const myData = [{ team: "GSW", pts: 120, ast: 18, reb: 11 }, { team: "GSW", pts: 125, ast: 28, reb: 18 }, { team: "GSW", pts: 110, ast: 35, reb: 47 }, { team: "HOU", pts: 100, ast: 17, reb: 43 }, { team: "HOU", pts: 102, ast: 14, reb: 32 }, { team: "SAS", pts: 127, ast: 21, reb: 25 }, { team: "SAS", pts: 135, ast: 25, reb: 37 }, { team: "SAS", pts: 142, ast: 18, reb: 27 }];

console.log(groupBy(myData, 'team'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 4 :(得分:0)

使用statsFields数组并循环遍历以创建总计,然后获取平均值

const myData = [
    {team: "GSW", pts: 120, ast: 18, reb: 11},
    {team: "GSW", pts: 125, ast: 28, reb: 18},
    {team: "GSW", pts: 110, ast: 35, reb: 47},
    {team: "HOU", pts: 100, ast: 17, reb: 43},
    {team: "HOU", pts: 102, ast: 14, reb: 32},
    {team: "SAS", pts: 127, ast: 21, reb: 25},
    {team: "SAS", pts: 135, ast: 25, reb: 37},
    {team: "SAS", pts: 142, ast: 18, reb: 27}
 ]
 
 const statsFields = ['pts','ast','reb'];
 
 const teamsObject = myData.reduce((a,{team,...stats})=>{
   a[team] = a[team] || {team, games:0};
   a[team].games++
   statsFields.forEach(k=> a[team][k] = (a[team][k] || 0) + stats[k]);
   return a;
 },{});
 
 const res = Object.values(teamsObject).map(({games,...team})=>{
    // average for each field total/games
    statsFields.forEach(k=> team[k] = team[k]/games);    
    return team;
 })
 
 console.log(JSON.stringify(res))

答案 5 :(得分:0)

可以很简单地完成以下操作。

注意:使用JSON.parse和stringify深浅复制数据。否则原始数组将被修改。如果可以修改原始数组,则不需要它。

const data = [
    {team: "GSW", pts: 120, ast: 18, reb: 11},
    {team: "GSW", pts: 125, ast: 28, reb: 18},
    {team: "GSW", pts: 110, ast: 35, reb: 47},
    {team: "HOU", pts: 100, ast: 17, reb: 43},
    {team: "HOU", pts: 102, ast: 14, reb: 32},
    {team: "SAS", pts: 127, ast: 21, reb: 25},
    {team: "SAS", pts: 135, ast: 25, reb: 37},
    {team: "SAS", pts: 142, ast: 18, reb: 27}
 ];

function groupData(mydata,keys)
{
    var accresult = mydata.reduce(function(acc, value){
      var arr = acc.filter(function(obj){return obj.team==value.team});
      arr.length ? (item=arr[0] , keys.forEach(function(key){ item[key]+=value[key]; })) : acc.push(value);
      return acc;
  },[]);

  var result = accresult.map(function(val){
      var l = mydata.filter(function(obj){return obj.team==val.team}).length;
      keys.forEach(function(key){ val[key]=(val[key]/l).toFixed(2); })
      return val;
  });
  return result;
}

console.log(groupData(JSON.parse(JSON.stringify(data.slice(0))),['pts','ast']));
console.log(groupData(JSON.parse(JSON.stringify(data.slice(0))),['pts','ast','reb']));
console.log(groupData(JSON.parse(JSON.stringify(data.slice(0))),['pts']));

答案 6 :(得分:0)

const myData = [
  { team: "GSW", pts: 120 },
  { team: "HOU", pts: 100 },
  { team: "GSW", pts: 110 },
  { team: "SAS", pts: 135 },
  { team: "HOU", pts: 102 },
  { team: "SAS", pts: 127 },
  { team: "SAS", pts: 142 },
  { team: "GSW", pts: 125 }
];

var result = myData.reduce(function (a, b) {
  var exist = -1;
  //some breaks the loop once it gets the true
  a.some((x, y) => {
    if (x.team == b.team) {
      //assigning index of existing object in array
      exist = y;
      return true;
    } else {
      return false;
    }
  });
  if (exist == -1) {
    a.push({ team: b.team, pts: b.pts, count: 1 });
  } else {
    a[exist].count += 1;
    a[exist].pts += b.pts;
  }
  return a;
}, []).map(t => {return {team: t.team, avg: t.pts/t.count, count:t.count}});


console.log(result);