Search code examples
javascriptjsxastrojs

Order by nested field using Astro


Given the following JSON response, how would I group them according to team first, then by year, using javascript and/or jsx to map the response? I'm using Astro.

{
  "data": [
    {
      "id": 1,
      "team_name": "Team One",
      "players": [
        {
          "player_name": "Mickey Mantle",
          "players_year": {
            "year": 2024
          }
        },
        {
          "player_name": "Larry Smalls",
          "players_year": {
            "year": 2022
          }
        },
        {
          "player_name": "Ron Davis",
          "players_year": {
            "year": 2024
          }
        }
      ]
    },
    {
      "id": 2,
      "team_name": "Team Two",
      "players": [
        {
          "player_name": "Jim Abbot",
          "players_year": {
            "year": 2022
          }
        }
      ]
    }
  ]
}

End Result – The HTML output should match this:

<div class="group">
  <h2>Team One</h2>
  <h3>2022</h3>
  <ul>
    <li>Larry Smalls</li>
  </ul>

  <h3>2024</h3>
  <ul>
    <li>Mickey Mantle</li>
    <li>Ron Davis</li>
  </ul>
</div>

<div class="group">
  <h2>Team Two</h2>
  <h3>2022</h3>
  <ul>
    <li>Jim Abbot</li>
  </ul>
</div>

Currently, here is my file which works, but without the grouping as seen directly above. What additional javascript is needed in Astro's frontmatter and markup required in the body of the page?

teams.astro

---
const directus = await getDirectusClient()

const response = await directus.items("teams").readByQuery({
    fields: [
        "id",
        "team_name",
        "players.player_name",
        "players.players_year.year",
    ],
})

const formattedResponse = response.data.map((team) => {
    return {
        ...team,
        yearsArr: team.players.map(player => player.players_year.year)
    }
})

const [...teams] = formattedResponse
---
<h1>Teams Page</h1>
{
  teams.map((team) => {
    return (
      <div class='group'>
        <h2>{team.team_name}</h2>
        <ul>
          {team.players.map((player) => (
            <li>
              {player.player_name} ({player.players_year.year})
            </li>
          ))}
        </ul>
      </div>
    )
  })
}

Solution

  • This have been asked many times before. But not exactly like this.

    Update: added the html markup (for Astro).

    var obj = {data:[{id:1,team_name:"Team One",players:[{player_name:"Mickey Mantle",players_year:{year:2024}},{player_name:"Larry Smalls",players_year:{year:2022}},{player_name:"Ron Davis",players_year:{year:2024}}]},{id:2,team_name:"Team Two",players:[{player_name:"Jim Abbot",players_year:{year:2022}}]}]};
    
    var result = {};
    obj.data.forEach(function(item) {
      result[item.team_name] = result[item.team_name] || {}
      item.players.forEach(function(player) {
        result[item.team_name][player.players_year.year] = result[item.team_name][player.players_year.year] || [];
        result[item.team_name][player.players_year.year].push(player.player_name);
      })
    })
    console.log(result)
    .as-console-wrapper {
      max-height: 100% !important
    }
    <h1>Teams Page</h1>
    {Object.entries(result).map(([team_name, team]) => { return (
    <div class='group'>
        <h2>{team_name}</h2>
    
        {Object.entries(team).map(([year, players]) => (
        <h3>{year}</h3>
        <ul>
            {players.map(player_name) => (
            <li>
                {player_name}
            </li>
            )}
        </ul>
        ))}
    
    </div>
    ) }) }