react same keys pushed into a table row depending on user Id

So i’m getting my data from an API which returns this:

appointments: [0: {customerId: 1, customerName: john, serviceName: serviceName1, start: {dateTime:"15:00"}, end: {dateTime:"16:00"}},
1: {customerId: 2, customerName: doe, serviceName: serviceName2, start: {dateTime:"14:00"}, end: {dateTime:"15:00"}},
2: {customerId: 2, customerName: doe,serviceName: serviceName3 , start: {dateTime:"12:00"}, end: {dateTime:"13:00"}}]

my snippet of code

render() {
return (
  <div>
    <Table>
      <thead>
        <tr>
          <th scope="col">room</th>
          <th scope="col">customer name</th>
          <th scope="col">serviceName1</th>
          <th scope="col">serviceName 2</th>
          <th scope="col">serviceName 3</th>
          <th scope="col">other</th>
        </tr>
      </thead>
      <tbody>
        {this.state.appointments.map((appointments: any, index) => {
          return (
            <tr key={appointments.customerId}>
              <td>{index + 1}</td>
              <td>{appointments.customerName}</td>
              {}
              <td>{`${formatDateTime(
                appointments.start?.dateTime
              )} - ${formatDateTime(appointments.end?.dateTime)}`}</td>
            </tr>
          );
        })}
      </tbody>
    </Table>
  </div>

My question is how can i push depending on the customer Id his both services (serviceName 2, and serviceName 3) into the same row and each row fills in the appropraite header the time for each service?

enter image description here

enter image description here

2 thoughts on “react same keys pushed into a table row depending on user Id”

  1. Try this, I have added index as key because in your case index is unique

    render() {
    return (
      <div>
        <Table>
          <thead>
            <tr key="heading">
              <th scope="col">room</th>
              <th scope="col">customer name</th>
              <th scope="col">serviceName1</th>
              <th scope="col">serviceName 2</th>
              <th scope="col">serviceName 3</th>
              <th scope="col">other</th>
            </tr>
          </thead>
          <tbody>
            {this.state.appointments.map((appointments: any, index) => {
              return (
                <tr key={index}>
                  <td>{index + 1}</td>
                  <td>{appointments.customerName}</td>
                  <td>{`${formatDateTime(
                    appointments.start?.dateTime
                  )} - ${formatDateTime(appointments.end?.dateTime)}`}</td>
                </tr>
              );
            })}
          </tbody>
        </Table>
      </div>
    
    
    Reply
  2. You can preprocess your data to reduce the duplicates into single entries to map in your UI. Merge the services and times into a nested array to map into additional services columns.

    Object.values(
      data.reduce(
        (dataObj, current) => ({
          ...dataObj,
          [current.customerId]: {
            ...dataObj[current.customerId],
            customerId: current.customerId,
            customerName: current.customerName,
            services: (dataObj[current.customerId]?.services || []).concat({
              serviceName: current.serviceName,
              start: current.start,
              end: current.end
            })
          }
        }),
        {}
      )
    );
    
    const data = [
      {
        customerId: 1,
        customerName: "john",
        serviceName: "serviceName1",
        start: { dateTime: "15:00" },
        end: { dateTime: "16:00" }
      },
      {
        customerId: 2,
        customerName: "doe",
        serviceName: "serviceName2",
        start: { dateTime: "14:00" },
        end: { dateTime: "15:00" }
      },
      {
        customerId: 2,
        customerName: "doe",
        serviceName: "serviceName3",
        start: { dateTime: "12:00" },
        end: { dateTime: "13:00" }
      }
    ];
    
    const processedData = Object.values(
      data.reduce(
        (dataObj, current) => ({
          ...dataObj,
          [current.customerId]: {
            ...dataObj[current.customerId],
            customerId: current.customerId,
            customerName: current.customerName,
            services: (dataObj[current.customerId]?.services || []).concat({
              serviceName: current.serviceName,
              start: current.start,
              end: current.end
            })
          }
        }),
        {}
      )
    );
    
    console.log(processedData);

    Map the processed appointments data.

    render() {
      return (
        <div>
          <Table>
            <thead>
              <tr>
                <th scope="col">room</th>
                <th scope="col">customer name</th>
                <th scope="col">serviceName1</th>
                <th scope="col">serviceName 2</th>
                <th scope="col">serviceName 3</th>
                <th scope="col">other</th>
              </tr>
            </thead>
            <tbody>
              {processedAppointments.map((appointments: any, index) => {
                return (
                  <tr key={appointments.customerId}>
                    <td>{index + 1}</td>
                    <td>{appointments.customerName}</td>
                    {
                      appointments.services.map((service) => (
                        <td key={service.serviceName}>
                          {`${formatDateTime(service.start?.dateTime)} - ${formatDateTime(service.end?.dateTime)}`}
                        </td>
                      ))
                    }
                  </tr>
                );
              })}
            </tbody>
          </Table>
        </div>
    

    Since it seems you’ve only 3 service columns, if your data should happen to include more than that then you should slice what you need from the processed data. I.e. something like appointments.services.slice(-3).map(... to grab up to the last 3.

    Reply

Leave a Comment