Home > front end >  Group javascript array by one property and then summarise by another
Group javascript array by one property and then summarise by another

Time:10-31

I'm trying to group an array by one property (year) and then create a nested array inside each year, listing the weeks with their total Total Distance and Max Avg Speed.

i.e.

[
    {
        "year": 2015,
        "weeks": [
            {week: 45, totalDistance: nnn, maxAvgSpeed: nnn},
            {week: 46, totalDistance: nnn, maxAvgSpeed: nnn},
            {week: 47, totalDistance: nnn, maxAvgSpeed: nnn}
            ...

This is what I have so far, but it says "activitiesByYear.forEach is not a function" even though activitiesByYear is an array as expected.

const activities = [
    {
        "id": 1,
        "actvityYear": 2015,
        "actvityWeek": 45,
        "name": "One",
        "avgSpeed": 1200,
        "distance": 2

    },
    {
        "id": 2,
        "actvityYear": 2015,
        "actvityWeek": 45,
        "name": "Two",
        "avgSpeed": 1403,
        "distance": 6
    },
    {
        "id": 3,
        "actvityYear": 2015,
        "actvityWeek": 46,
        "name": "Three",
        "avgSpeed": 1700,
        "distance": 7
    },
    {
        "id": 4,
        "actvityYear": 2015,
        "actvityWeek": 47,
        "name": "Four",
        "avgSpeed": 600,
        "distance": 12
    },
    {
        "id": 5,
        "actvityYear": 2015,
        "actvityWeek": 47,
        "name": "Five",
        "avgSpeed": 300,
        "distance": 4
    },
    {
        "id": 6,
        "actvityYear": 2016,
        "actvityWeek": 2,
        "name": "Six",
        "avgSpeed": 1800,
        "distance": 15
    }
]

function groupBy(objectArray, property) {
    return objectArray.reduce((acc, obj) => {
    const key = obj[property];
    if (!acc[key]) {
        acc[key] = [];
    }
    // Add object to list for given key's value
    acc[key].push(obj);
    return acc;
    }, {});
}

const activitiesByYear = groupBy(activities, 'actvityYear');
let years = [];
activitiesByYear.forEach(year => {
    let weeks = [];
    year.reduce(function(result, value) {
        const week = moment(value.actvityDate).week();
        if (!result[week]) {
            result[week] = { week: week, totalDistance: 0, maxAvgSpeed: value.avgSpeed };
            weeks.push(result[week])
        }
        result[week].totalDistance  = value.distance;
        if (value.average_speed > result[week].maxAvgSpeed) result[week].maxAvgSpeed = value.average_speed;
        return result;
    }, {});
    years.push({"year": year, "weeks": weeks})
})

console.log(years)

CodePudding user response:

You get TypeError: activitiesByYear.forEach is not a function because activitiesByYear is a object with the following keys: [ '2015', '2016' ]. Because the groupBy returns an object. You can get the keys of the object with Object.keys().

For example:

const keys = Object.keys(activitiesByYear)
// [ '2015', '2016' ]

CodePudding user response:

Use Object#entries to iterate over the year-items pairs

const activities = [
  { "id": 1, "actvityYear": 2015, "actvityWeek": 45, "name": "One", "avgSpeed": 1200, "distance": 2 },
  { "id": 2, "actvityYear": 2015, "actvityWeek": 45, "name": "Two", "avgSpeed": 1403, "distance": 6 },
  { "id": 3, "actvityYear": 2015, "actvityWeek": 46, "name": "Three", "avgSpeed": 1700, "distance": 7 },
  { "id": 4, "actvityYear": 2015, "actvityWeek": 47, "name": "Four", "avgSpeed": 600, "distance": 12 },
  { "id": 5, "actvityYear": 2015, "actvityWeek": 47, "name": "Five", "avgSpeed": 300, "distance": 4 },
  { "id": 6, "actvityYear": 2016, "actvityWeek": 2, "name": "Six", "avgSpeed": 1800, "distance": 15 }
];

function groupBy(objectArray, property) {
  return objectArray.reduce((acc, obj) => {
    const key = obj[property];
    if (!acc[key]) { acc[key] = []; }
    acc[key].push(obj);
    return acc;
  }, {});
}

const activitiesByYear = groupBy(activities, 'actvityYear');
let years = [];
Object.entries(activitiesByYear).forEach(([year, items]) => { // fix
  let weeks = [];
  items.reduce((result, value) => {
    const week = value.actvityWeek;
    if (!result[week]) {
      result[week] = { week, totalDistance: 0, maxAvgSpeed: value.avgSpeed };
      weeks.push(result[week])
    }
    result[week].totalDistance  = value.distance;
    if (value.average_speed > result[week].maxAvgSpeed) {
      result[week].maxAvgSpeed = value.average_speed;
    }
    return result;
  }, {});
  years.push({ year, weeks });
});

console.log(years);

  • Related