Skip to content
Advertisement

React: Separate array of timestamps into days

My API (NotesData) returns an array of objects where one of the key/value pairs is a PostgreSQL timestamp formatted like 2022-04-12T19:25:39.950747+00:00.

I am creating a timeline of notes, where each object in the array is its own note with its own timestamp. So far, I have mapped over the array to create a timeline of notes, with the time of note creation next to each of the notes.

I’m trying to figure out how I can separate these notes up by the days they were created. For example, all of the notes created on 18/04/2022 will come under a heading of Monday 18th April, and so on.

So far, I have three functional components, the main Timeline (NotesStream), NoteCard (which is essentially the note with the HH:MM next to it), and the NoteDate. NoteDate is the component I’m struggling to figure out how to conditionally render.

Essentially, I need one NoteDate component rendered for every NoteCard component that contains the same date timestamp.

Many thanks!

Timeline:

import NoteCard from './NoteCard';
import NoteDate from './NoteDate';

function NotesStream({
  notesData,
  deleteNote,
}: {
  notesData: any;
  deleteNote: any;
}) {
  return (
    <div className="notes-stream column">
      {notesData && (
        <>
          {notesData.map((note: any) => (
            <NoteCard
              key={note.id}
              id={note.id}
              content={note.note_content}
              time={note.created_at}
              deleteNote={deleteNote}
            />
          ))}
        </>
      )}
    </div>
  );
}

export default NotesStream;

Note Card:

function NoteCard({
  id,
  content,
  time,
  deleteNote,
}: {
  id: number;
  content: string;
  time: string;
  deleteNote: any;
}) {
  const dateTime = new Date(time);

  return (
    <div className="note-card note-card__alternate">
      <div className="note-card--content">
        <p>{content}</p>
      </div>
      <div className="note-card--time note-card--time__alternate">
        <span>
          {dateTime.toLocaleTimeString('uk', {
            hour: '2-digit',
            minute: '2-digit',
          })}
        </span>
      </div>
      <div className="note-card--delete">
        <p
          onClick={() => {
            deleteNote(id);
          }}
        >
          Delete
        </p>
      </div>
    </div>
  );
}

export default NoteCard;

Note Date:

function NoteDate({ date }: { date: string }) {
  return <h2 className="notes-date">{date}</h2>;
}

export default NoteDate;

Advertisement

Answer

You can achieve that by first re-shaping the data using the reduce function, to group your notes by date. The following snippet demonstrates a possible implementation:

const groupByDate = (notesData) => {
  return notesData.reduce((groups, note) => {
    const date = new Date(note.timestamp).toDateString()
    if (!groups.hasOwnProperty(date)) {
      groups[date] = []
    }
    groups[date].push(note)
    return groups
  }, {})
  
}

const testData = [
  {
    timestamp: "2022-04-12T19:25:39.950747+00:00",
    name : "note 1"
  },
  {
    timestamp: "2022-04-12T19:25:39.950747+00:00",
    name : "note 2"
  }, {
    timestamp: "2022-05-12T19:25:39.950747+00:00",
    name : "note 3"
  }, {
    timestamp: "2022-05-12T19:25:39.950747+00:00",
    name : "note 4"
  }

]
console.log(groupByDate(testData))

Then you can render on NoteDate for each key of the resulting object, and a NoteCard for each Note.

Keep in mind that this is an expensive operations, an you might need to memoize it with useMemo to avoid recomputing the grouped date with each render.

Finally if you have access to the API code from which your getting the information, the correct way to handle this would probably be to do it server-side. Grouping the notes by date would be way more performant if it was done by PostgreSQL !

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement