apTNow
apTNow

Reputation: 825

Javascript ES5: How to Group By a key and create Sub objects from a flat array of key-value objects without using Lodash or Underscore

I have an array of Objects with 4 keys:

[
  {
    "team": "USA",
    "team_profile_id": "10",
    "player": "Captain America",
    "player_id": "10X1"
  },
  {
    "team": "USA",
    "team_profile_id": "10",
    "player": "The hulk",
    "player_id": "10X2"
  },
  {
    "team": "India",
    "team_profile_id": "20",
    "player": "Captain America",
    "player_id": "10X1"
  },
  {
    "team": "India",
    "team_profile_id": "20",
    "player": "SpiderMan",
    "player_id": "10X5"
  }
]

And I want to group my array by "Teams" such that it creates an array of Player objects for each team

 [
  {
    "team": "USA",
    "team_profile_id": "10",
    "Players": [
      {
        "player": "Captain America",
        "player_id": "10X1"
      },
      {
        "player": "The hulk",
        "player_id": "10X2"
      }
    ]
  },
  {
    "team": "India",
    "team_profile_id": "20",
    "Players": [
      {
        "player": "Captain America",
        "player_id": "10X1"
      },
      {
        "player": "SpiderMan",
        "player_id": "10x5"
      }
    ]
  }
]

I am currently trying to use reduce function from MDN prototype but it is not able to handle the sub object creation part.

Currently I have tried:

var group_to_values = result.reduce(function(obj, item) {
            obj[item.team] = obj[item.team] || [];
            obj[item.team].push({
                "team_profile_id":item.team_profile_id,
                "player":item.player,
                "player_id":item.player_id
            });
            return obj;
        }, {});

But it is not giving the desired result

Upvotes: 0

Views: 874

Answers (7)

Ori Drori
Ori Drori

Reputation: 191976

While you iterate the players with Array.reduce(), destructure the player object, and separate between the player's properties, and team using spread.

If the team doesn't exist in the accumulator (r), create a new object with the team's properties, and the Player: [] property. Push the players into the Players array of the relevant team. Use Object.values() to convert back to an array.

const data = [{"team":"USA","team_profile_id":"10","player":"Captain America","player_id":"10X1"},{"team":"USA","team_profile_id":"10","player":"The hulk","player_id":"10X2"},{"team":"India","team_profile_id":"20","player":"Captain America","player_id":"10X1"},{"team":"India","team_profile_id":"20","player":"SpiderMan","player_id":"10X5"}]

const result = Object.values(data.reduce((r, { team, team_profile_id, ...player }) => {
  if(!r[team]) r[team] = { team, team_profile_id, Players: [] }
  
  r[team].Players.push(player)

  return r
}, {}))

console.log(result)

How to ES5 this solution:

  • The main problem is Object.values(), but we can generate a values() functions using Object.keys().
  • Replace consts with vars, arrow functions with functions, destructuring with manual manual assignments.

var data = [{"team":"USA","team_profile_id":"10","player":"Captain America","player_id":"10X1"},{"team":"USA","team_profile_id":"10","player":"The hulk","player_id":"10X2"},{"team":"India","team_profile_id":"20","player":"Captain America","player_id":"10X1"},{"team":"India","team_profile_id":"20","player":"SpiderMan","player_id":"10X5"}];

var result = values(data.reduce(function(r, o) {  
  var team = o.team;

  if(!r[team]) r[team] = {
    team: team,
    team_profile_id: o.team_profile_id,
    Players: [] 
  };
  
  r[team].Players.push({ player: o.player, player_id: o.player_id })

  return r
}, {}))

function values(obj) {
  return Object.keys(obj).map(function(key) {
    return obj[key];
  });
}

console.log(result)

Upvotes: 1

Kwame Opare Asiedu
Kwame Opare Asiedu

Reputation: 2355

Try this...

const playerData = [ ... ]
const teamGroups = playerData.reduce ((teamData, playerObj) => {
    const targetTeam = teamData.filter(t => t.team === playerObj.team)[0];

    if (!targetTeam) {
        return [ ...teamData, { 
            team: playerObj.team, 
            team_profile_id: playerObj.team_profile_id, 
            Players: [{ 
                player: playerObj.player, 
                player_id: playerObj.player_id 
            }] 
        }];
    }

    targetTeam.Players.push ({ player: playerObj.player, player_id: playerObj.player_id });
    return teamData;
}, []);

Upvotes: 0

Adam Patterson
Adam Patterson

Reputation: 958

Try this:

'use strict';

const inputData = [
  {
    "team": "USA",
    "team_profile_id": "10",
    "player": "Captain America",
    "player_id": "10X1"
  },
  {
    "team": "USA",
    "team_profile_id": "10",
    "player": "The hulk",
    "player_id": "10X2"
  },
  {
    "team": "India",
    "team_profile_id": "20",
    "player": "Captain America",
    "player_id": "10X1"
  },
  {
    "team": "India",
    "team_profile_id": "20",
    "player": "SpiderMan",
    "player_id": "10X5"
  }
];

function groupByTeams(data) {
  // Find the teams
  const teams = data.map(({ team }) => team);
  const uniqueTeams = [...new Set(teams)];

  // Map the array of unique values to return
  // desired result.
  return uniqueTeams.map(team => {
    return {
      team,
      team_profile_id: data.find((thisTeam) => thisTeam.team === team).team_profile_id,
      Players: data
        .filter(thisTeam => thisTeam.team === team)
        .map(({ player, player_id }) => ({ player_id, player }))
    }
  });
}

groupByTeams(inputData);

Upvotes: 0

Andre Nuechter
Andre Nuechter

Reputation: 2255

Try the following. First I create a set of unique team names and then filter the data based on that to get a list of relevant players:

const data = [
  {
    "team": "USA",
    "team_profile_id": "10",
    "player": "Captain America",
    "player_id": "10X1"
  },
  {
    "team": "USA",
    "team_profile_id": "10",
    "player": "The hulk",
    "player_id": "10X2"
  },
  {
    "team": "India",
    "team_profile_id": "20",
    "player": "Captain America",
    "player_id": "10X1"
  },
  {
    "team": "India",
    "team_profile_id": "20",
    "player": "SpiderMan",
    "player_id": "10X5"
  }
];

const res = [...new Set(data.map(e => e.team))]
    .map(teamName => ({ 
        team: teamName, 
        team_profile_id: data.find(e => e.team === teamName).team_profile_id,
        Players: data
            .filter(p => p.team === teamName)
            .map(p => ({ 
                player: p.player,
                player_id: p.player_id
                }))
            }));

console.log(res);

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386570

You could find the object and add the new player.

var data = [{ team: "USA", team_profile_id: "10", player: "Captain America", player_id: "10X1" }, { team: "USA", team_profile_id: "10", player: "The hulk", player_id: "10X2" }, { team: "India", team_profile_id: "20", player: "Captain America", player_id: "10X1" }, { team: "India", team_profile_id: "20", player: "SpiderMan", player_id: "10X5" }],
    result = data.reduce((r, { team, team_profile_id, player, player_id }) => {
        var temp = r.find(q => q.team_profile_id === team_profile_id);
        if (!temp) r.push(temp = { team, team_profile_id, Players: [] });
        temp.Players.push({ player, player_id });
        return r;
    }, []);
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Lauren Yim
Lauren Yim

Reputation: 14088

This is probably not the best way to do it but it works.

const players = [
  {
    "team": "USA",
    "team_profile_id": "10",
    "player": "Captain America",
    "player_id": "10X1"
  },
  {
    "team": "USA",
    "team_profile_id": "10",
    "player": "The hulk",
    "player_id": "10X2"
  },
  {
    "team": "India",
    "team_profile_id": "20",
    "player": "Captain America",
    "player_id": "10X1"
  },
  {
    "team": "India",
    "team_profile_id": "20",
    "player": "SpiderMan",
    "player_id": "10X5"
  }
]

const teams = players.reduce((arr, player) => {
  let [team] = arr.filter(t => t.team === player.team)
  if (!team) {
    team = {
      team: player.team,
      team_profile_id: player.team_profile_id,
      Players: []
    }
    arr.push(team)
  }

  team.Players.push({
    player: player.player,
    player_id: player.player_id
  })
  
  return arr
}, [])

console.log(teams)

Upvotes: 0

antonku
antonku

Reputation: 7665

Since the result needs to be an array you can use it as the initial value for reduce instead of an object:

var players = [
  {
    "team": "USA",
    "team_profile_id": "10",
    "player": "Captain America",
    "player_id": "10X1"
  },
  {
    "team": "USA",
    "team_profile_id": "10",
    "player": "The hulk",
    "player_id": "10X2"
  },
  {
    "team": "India",
    "team_profile_id": "20",
    "player": "Captain America",
    "player_id": "10X1"
  },
  {
    "team": "India",
    "team_profile_id": "20",
    "player": "SpiderMan",
    "player_id": "10X5"
  }
]

var teams = players.reduce(function (teams, player) {
  var matchingTeams = teams.filter(function(team) {
    return team.team === player.team;
  });
  var team;
  if (matchingTeams.length) {
    team = matchingTeams[0]
  } else {
    team = {}
    teams.push(team)
  }
  team.team = player.team;
  team.team_profile_id = player.team_profile_id;
  var teamPlayer = {
    player: player.player,
    player_id: player.player_id
  };
  team.players = team.players ? team.players.concat(teamPlayer) : [teamPlayer];
  return teams
}, [])

console.log(teams)

Upvotes: 1

Related Questions