Sort array within another array with Reactjs

Asked

Viewed 258 times

0

Staff I set up a table with the data of an array where will have attack and defense points of each player associated to a team. You will have the total points of attack and defenses of each player and a final total that adds up the total of attack and the total of defense of each player. Below an image to understand what I am saying:

inserir a descrição da imagem aqui

My problem is I want to sort the "SUM A/B" column down. I know javascript has the Sort method, but can not use for my problem. Below an image of how I want it to look.

inserir a descrição da imagem aqui

Thank you if someone will at least give me an idea. My code below:

console.clear()

const Debug = () => {
    const [players, setPlayers] = React.useState([
        {
            name: 'Tiago',
            teams: [
                { name: 'Invictos', attack: 18, defense: 5 },
                { name: 'Warrions', attack: 10, defense: 2 },
            ],
        },
        {
            name: 'Carlos',
            teams: [
                { name: 'Invictos', attack: 13, defense: 7 },
                { name: 'Warrions', attack: 9, defense: 3 },
            ],
        },
        {
            name: 'Ana',
            teams: [
                { name: 'Invictos', attack: 18, defense: 8 },
                { name: 'Warrions', attack: 17, defense: 5 },
            ],
        },
    ])

    return (
        <div>
            <table style={{ textAlign: 'center', border: '1px solid #000' }}>
                <thead>
                    <tr>
                        <th rowSpan="2">#</th>
                        <th rowSpan="2">Nome</th>
                        <th colSpan="2">Invictos</th>
                        <th colSpan="2">Warrions</th>
                        <th colSpan="2">Total</th>
                        <th>Soma A/D</th>
                    </tr>

                    <tr>
                        <td>Ataques</td>
                        <td>Defesas</td>
                        <td>Ataques</td>
                        <td>Defesas</td>
                        <td>Ataques</td>
                        <td>Defesas</td>
                    </tr>
                </thead>
                <tbody>
                    {players.map((player, index) => (
                        <tr key={index}>
                            <td>{index + 1}</td>
                            <td>{player.name}</td>
                            {player.teams.map((p, i) => (
                                <React.Fragment key={i}>
                                    <td>{p.attack}</td>
                                    <td>{p.defense}</td>
                                </React.Fragment>
                            ))}
                            <td>{player.teams.reduce((a, b) => a + b.attack, 0)}</td>
                            <td>{player.teams.reduce((a, b) => a + b.defense, 0)}</td>
                            <td>
                                {player.teams.reduce((a, b) => a + b.attack, 0) +
                                    player.teams.reduce((a, b) => a + b.defense, 0)}
                            </td>
                        </tr>
                    ))}
                </tbody>
            </table>
        </div>
    )
}

ReactDOM.render(<Debug />, document.querySelector('#root'))
<div id="root"></div>

<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

  • increasing or decreasing? You spoke one thing and in the image you showed another. Why not create a new state that stores objects with calculated values and then render from this new object?

  • Hello friend, it’s even decreasing, already fix was typo. I thought about it, but couldn’t find a way to concatenate with the rest of the data. Could you show me an example?

2 answers

2


Create a new state called sumary to summarise the array state-owned players and the same operation that is done directly in the table does in some method or when loading the component. In summarizing the results put the sort function by the field sum established as demonstrated in that code:

React.useEffect(() => {
  const items = players.map(p => {
    const attack = p.teams
      .reduce((a, b) => a + b.attack, 0);
    const defense = p.teams
      .reduce((a, b) => a + b.defense, 0);        
    const sum = attack + defense;          
    return {
      name: p.name,
      teams: [...p.teams],
      attack,
      defense,
      sum
    }
  });
  items.sort((a,b) => b.sum - a.sum);
  setSumary(state => [...items]);  
}, [players]);

and from this code I made the complete example with the two tables:

const Debug = () => {
    const [sumary, setSumary] = React.useState([]);
    const [players, setPlayers] = React.useState([
        {
            name: 'Tiago',
            teams: [
                { name: 'Invictos', attack: 18, defense: 5 },
                { name: 'Warrions', attack: 10, defense: 2 },
            ],
        },
        {
            name: 'Carlos',
            teams: [
                { name: 'Invictos', attack: 13, defense: 7 },
                { name: 'Warrions', attack: 9, defense: 3 },
            ],
        },
        {
            name: 'Ana',
            teams: [
                { name: 'Invictos', attack: 18, defense: 8 },
                { name: 'Warrions', attack: 17, defense: 5 },
            ],
        },
    ])
    React.useEffect(() => {
      const items = players.map(p => {
        const attack = p.teams
          .reduce((a, b) => a + b.attack, 0);
        const defense = p.teams
          .reduce((a, b) => a + b.defense, 0);        
        const sum = attack + defense;          
        return {
          name: p.name,
          teams: [...p.teams],
          attack,
          defense,
          sum
        }
      });
      items.sort((a,b) => b.sum - a.sum);
      setSumary(state => [...items]);            
    }, [players]);
    return (
        <div>
            <table style={{ textAlign: 'center', border: '1px solid #000' }}>
                <thead>
                    <tr>
                        <th rowSpan="2">#</th>
                        <th rowSpan="2">Nome</th>
                        <th colSpan="2">Invictos</th>
                        <th colSpan="2">Warrions</th>
                        <th colSpan="2">Total</th>
                        <th>Soma A/D</th>
                    </tr>

                    <tr>
                        <td>Ataques</td>
                        <td>Defesas</td>
                        <td>Ataques</td>
                        <td>Defesas</td>
                        <td>Ataques</td>
                        <td>Defesas</td>
                    </tr>
                </thead>
                <tbody>
                    {players.map((player, index) => (
                        <tr key={index}>
                            <td>{index + 1}</td>
                            <td>{player.name}</td>
                            {player.teams.map((p, i) => (
                                <React.Fragment key={i}>
                                    <td>{p.attack}</td>
                                    <td>{p.defense}</td>
                                </React.Fragment>
                            ))}
                            <td>{player.teams.reduce((a, b) => a + b.attack, 0)}</td>
                            <td>{player.teams.reduce((a, b) => a + b.defense, 0)}</td>
                            <td>
                                {player.teams.reduce((a, b) => a + b.attack, 0) +
                                    player.teams.reduce((a, b) => a + b.defense, 0)}
                            </td>
                        </tr>
                    ))}
                </tbody>
            </table>
            <hr/>
            <table style={{ textAlign: 'center', border: '1px solid #000' }}>
                <thead>
                    <tr>
                        <th rowSpan="2">#</th>
                        <th rowSpan="2">Nome</th>
                        <th colSpan="2">Invictos</th>
                        <th colSpan="2">Warrions</th>
                        <th colSpan="2">Total</th>
                        <th>Soma A/D</th>
                    </tr>

                    <tr>
                        <td>Ataques</td>
                        <td>Defesas</td>
                        <td>Ataques</td>
                        <td>Defesas</td>
                        <td>Ataques</td>
                        <td>Defesas</td>
                    </tr>
                </thead>
                <tbody>
                    {sumary.map((player, index) => (
                        <tr key={index}>
                            <td>{index + 1}</td>
                            <td>{player.name}</td>
                            {player.teams.map((p, i) => (
                                <React.Fragment key={i}>
                                    <td>{p.attack}</td>
                                    <td>{p.defense}</td>
                                </React.Fragment>
                            ))}
                            <td>{player.attack}</td>
                            <td>{player.defense}</td>
                            <td>{player.sum}</td>
                        </tr>
                    ))}
                </tbody>
            </table>
        </div>
    )
}

ReactDOM.render(<Debug />, document.querySelector('#root'))
<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

<div id="root"></div>

In short: create another array with the summary and show it with the data already calculated.

  • 1

    Dude, you’re a genius. I’ve been trying to figure this out for a while and your answer came right out with what you wanted. Thank you very much.

2

I will give an alternative solution to another answer, because she uses the useEffect and don’t know if you intend to use this hook.

Basically you can follow 3 steps:

  1. Create a function that calculates SomaAD for each of the players and returns an array with the value of that calculation. I called somaAD, for example.
  2. Crair a function that sorts the array returned by the top function and returns the ordered array decreasingly based on the value of somaAD.
  3. Create a function that renders the calculated and ordered array.

The function that calculates somaAD for each of the players:

  const calculateSomaAD = (players) => {
    return players.map(player => {
      return {
        ...player, somaAD: (player.teams.reduce((a, b) => a + b.attack, 0) +
          player.teams.reduce((a, b) => a + b.defense, 0))
      };
    });
  };

The function that sorts the array based on the value of somaAD and returns the ordered array:

  const orderPlayers = (arr) => {
    return arr.sort((a, b) => b.somaAD - a.somaAD); // decrescente
  };

The function that renders the calculated and ordered array:

  const render = () => { // funcao que monta os componentes
    const array = calculateSomaAD(players); // calcula somaAD primeiro
    const orderedArray = orderPlayers(array); // ordena o array

    return orderedArray.map((player,index) => ( // monta as <tr>
      <tr key={index}>
        <td>{index + 1}</td>
        <td>{player.name}</td>
        {player.teams.map((p, i) => (
          <React.Fragment key={i}>
            <td>{p.attack}</td>
            <td>{p.defense}</td>
          </React.Fragment>
        ))}
        <td>{player.teams.reduce((a, b) => a + b.attack, 0)}</td>
        <td>{player.teams.reduce((a, b) => a + b.defense, 0)}</td>
        <td>
          {player.somaAD}
        </td>
      </tr>
    ));
   };

Complete code:

console.clear();

const Debug = () => {
  const [players, setPlayers] = React.useState([
    {
      name: 'Tiago',
      teams: [
        { name: 'Invictos', attack: 18, defense: 5 },
        { name: 'Warrions', attack: 10, defense: 2 },
      ],
    },
    {
      name: 'Carlos',
      teams: [
        { name: 'Invictos', attack: 13, defense: 7 },
        { name: 'Warrions', attack: 9, defense: 3 },
      ],
    },
    {
      name: 'Ana',
      teams: [
        { name: 'Invictos', attack: 18, defense: 8 },
        { name: 'Warrions', attack: 17, defense: 5 },
      ],
    },
  ]);

  const calculateSomaAD = (players) => {
    return players.map(player => {
      return {
        ...player, somaAD: (player.teams.reduce((a, b) => a + b.attack, 0) +
          player.teams.reduce((a, b) => a + b.defense, 0))
      };
    });
  };

  const orderPlayers = (arr) => {
    return arr.sort((a, b) => b.somaAD - a.somaAD);
  };

  const render = () => {
    const array = calculateSomaAD(players);
    const orderedArray = orderPlayers(array);

    return orderedArray.map((player,index) => (
      <tr key={index}>
        <td>{index + 1}</td>
        <td>{player.name}</td>
        {player.teams.map((p, i) => (
          <React.Fragment key={i}>
            <td>{p.attack}</td>
            <td>{p.defense}</td>
          </React.Fragment>
        ))}
        <td>{player.teams.reduce((a, b) => a + b.attack, 0)}</td>
        <td>{player.teams.reduce((a, b) => a + b.defense, 0)}</td>
        <td>
          {player.somaAD}
        </td>
      </tr>
    ));
  };

  return (
    <div>
      <table style={{ textAlign: 'center', border: '1px solid #000' }}>
        <thead>
          <tr>
            <th rowSpan="2">#</th>
            <th rowSpan="2">Nome</th>
            <th colSpan="2">Invictos</th>
            <th colSpan="2">Warrions</th>
            <th colSpan="2">Total</th>
            <th>Soma A/D</th>
          </tr>

          <tr>
            <td>Ataques</td>
            <td>Defesas</td>
            <td>Ataques</td>
            <td>Defesas</td>
            <td>Ataques</td>
            <td>Defesas</td>
          </tr>
        </thead>
        <tbody>
          {/* renderizamos aqui a tabela ordenada de forma decrescente */}
          {render()}
        </tbody>
      </table>
    </div>
  );
};

ReactDOM.render(<Debug />, document.querySelector('#root'));
<div id="root"></div>

<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

See what I used:

<tbody>
 {render()}
</tbody>

because I don’t particularly often insert very complex logics into the code that will be rendered. I find it easier to understand this way, creating a separate function that mounts the component to later render.

  • 1

    Very good guy, helped a lot. But for my problem useEffect is necessary because these data are coming from a database. I put a part of them in the players for better understanding. I really liked the way you organize separating the most complex part of the render. Thank you very much for your attention.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.