2

I'm building a table that is to display the user's hours associated to a particular day of the month. Since each month has different days, the columns are set dynamically. I've got the structure of it fine, with the table head displaying the appropriate number of days depending on the month and year selected. I have the data being pulled by the api and being displayed, however I'm at a total loss as how to match the dates from the data to the days in the columns.

I've made a simplified codesandbox which shows where I'm at: https://codesandbox.io/s/react-table-dynamic-columns-mr4u6w?file=/src/App.js:0-1890

To further complicate it, the data I'm working with on the actual app has 2 date objects, a start and end time. Generally the hours should not cross days, but that could happen. For this table I figure I can get away with pulling the date from one of the date objects (which is why the sandbox has only a start field).

I've considered trying to implement something like

setDataDates(
   const dataDay = new Date(data.start).getDate()

to get the day of the visit, but I'm totally lost on how to then provide that to the correct day on the table. Any guidance would be greatly appreciated, thanks!

UPDATE So I think I'm close, and it should be rendering at least, but sandbox is giving the error: Objects are not valid as a React child (found: object with keys {day}), where I'm mapping the days in the table header. This part has been functioning fine before, so I'm lost as to why its what is broken now... (this aspect has been fixed, because I made the array {key: value}, I forgot to call it in the .map as {day.day}. It is updated below

The actual functionality is still not there however

import "./styles.css";
import React from "react";

export default function App() {
  const data = [
    {
      start: "2022-04-02",
      hours: 2
    },
    {
      start: "2022-04-06",
      hours: 3
    },
    {
      start: "2022-04-11",
      hours: 1
    }
  ];
  const [newData, setNewData] = React.useState();

  const setDataforTable = () => {
    const oldData = data.map((data) => ({
      day: new Date(data.start).getDate(),
      hours: data.hours
    }));
    console.log("newData", oldData);
    setNewData(oldData);
  };

  const [days, setDays] = React.useState([]);

  const settingDays = () => {
    const daysInMonth = 30;

    let dayInfo = [];
    for (let i = 1; i <= daysInMonth; i++) {
      dayInfo.push(i);

     let result = dayInfo.map((i) => (
       {
        day: i
      }
      ));
      console.log("day ", result);
      setDays(result);
      
    }
  };

  const totalHours = data.map((i) => i.hours).reduce((a, b) => a + b, 0);

  let tableDisplay;


  if (days !== []) {
    
    tableDisplay = (
      <div>
        <table>
          <thead>
            <tr>
              <th> Date </th>
              {days && days.map((day) => <th key={day}>{day.day}</th>)}
              <th>Total Hours</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Hours</td>
              {newData && newData.map((d) => 
              <td key={d.day}>{d.day === days.day ? d.hours : 0}</td>
              )}
              <td>{totalHours}</td>
            </tr>
          </tbody>
        </table>
      </div>
    );
  }

  return (
    <div>
      <button
        onClick={() => {
          setDataforTable(), settingDays();
        }}
      >
        Click
      </button>
      {tableDisplay}
    </div>
  );
}
2

1 Answer 1

1

So, here's the working solution and you could achieve what you wanted by making some changes. This solution is not fully optimized but you'll get the idea.

So, in order to get the results you'll have to create an new array of data with actual date number so we could use it later for filtering the dayArr. After that you'll have to map over your day array of object and check if the day matches with day inside data array if it matches then replace the hours.

import "./styles.css";
import React from "react";

export default function App() {
  const data = [
    {
      start: "2022-04-02",
      hours: 2
    },
    {
      start: "2022-04-06",
      hours: 3
    },
    {
      start: "2022-04-11",
      hours: 1
    }
  ];


  // filter the data so we can have actual day number
  const filteredData = data.map((item) => {
    const splitDate = item.start.split("-");
    const day = splitDate[splitDate.length - 1];

    return {
      start: parseInt(day),
      hours: item.hours
    };
  });


  const [newData, setNewData] = React.useState();

  const setDataforTable = () => {
    const dataDays = data.map((data) => ({
      day: new Date(data.start).getDate(),
      hours: data.hours
    }));

    setNewData(dataDays);
  };

  const [days, setDays] = React.useState([]);

  const settingDays = () => {
    const daysInMonth = 30;

    let dayInfo = [];
    for (let i = 1; i <= daysInMonth; i++) {
      dayInfo.push(i)

      // create an array and fill hours with 0
      const dayArr = dayInfo.map((day) => ({
        day: day,
        hours: 0
      }));


      // map over dayArr and replace the hours 
      let temp = [...dayArr];
      dayArr.map((i, idx) => {
        const getHours = filteredData.filter((item) => item.start === i.day);
        if (getHours.length) {
          const newData = {
            day: i.day,
            hours: getHours[0].hours
          };
          temp[idx] = newData;
        }
      });

      setDays(temp);
    }
  };

  const totalHours = data.map((i) => i.hours).reduce((a, b) => a + b, 0);

  let tableDisplay;


  if (days.length) {
    //convertData();
    tableDisplay = (
      <div>
        <table>
          <thead>
            <tr>
              <th> Date </th>
              {days.map((i) => (
                <th key={i}>{i?.day}</th>
              ))}
              <th>Total Hours</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Hours</td>
              {/* {newData &&
                
              } */}
              {days && days.map((d) => <td>{d.hours}</td>)}
              <td>{totalHours}</td>
            </tr>
          </tbody>
        </table>
      </div>
    );
  }

  return (
    <div>
      <button
        onClick={() => {
          setDataforTable();
          settingDays();
        }}
      >
        Click
      </button>
      {tableDisplay}
    </div>
  );
}
Sign up to request clarification or add additional context in comments.

3 Comments

Beautiful! Thank you, I was struggling to wrap my head around comparing the arrays. Also I appreciate your use of using filter to get the date rather than getDate(), it made me recognize that since on my actual app I'm using ISO strings I'll need to make sure the dates are landing where they should. I've updated the sandbox for anyone who comes along and needs a working example. Thanks again!
A follow up question came to mind, what if there were multiple entries for the same day? Such as multiple the user having multiple hours at different times in the day, but on the same day. I recognize that could become a whole new question potentially, but if you have an idea of how to approach, Id appreciate it
Figured it out based on this answer to another person's question: stackoverflow.com/a/58086559/14330332 . I've updated the sandbox for anyone who needs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.