0

I have data in an array of objects as below:

history = [
  {item: cake, calories: 120, datetime: 2022-11-16 07:51:26},
  {item: chicken, calories: 250, datetime: 2022-11-16 13:48:46},
  {item: pizza, calories: 420, datetime: 2022-11-25 11:13:42}
];

I want to render a div with a heading for the date and group all items with same dates in a list. I am using my map function like below:

function renderHistory () {
  let date;

  return props.history.map((item, i) => {
    const dateAdded = item.datetime.split(" ")[0];

    if (date !== dateAdded) {
      date = dateAdded;
      return (
        <>
          <div>
            <h2>{dateAdded}</h2>
            <li>{item.item} - {item.calories} calories</li>
        </>
      )
    }

    return (
      <>
          <li>{item.item} - {item.calories} calories</li>
        </div>
      </>
    )

  })
}

I get an error Expected corresponding JSX closing tag for <div>

If I return like below the second item gets out of the div.

return props.history.map((item, i) => {

  const dateAdded = item.datetime.split(" ")[0];

  if (date !== dateAdded) {
    date = dateAdded;
    return (
      <div>
        <h2>{dateAdded}</h2>
        <li>{item.item} - {item.calories} calories</li>
      </div>
    )
  }

  return (
    <li>{item.item} - {item.calories} calories</li>
  )

})

How do I put all items with same date in a single div?

DragonBorn
  • 1,809
  • 5
  • 20
  • 44
  • `return (
    { props.history.map( ... )}
    )`
    – derpirscher Nov 25 '22 at 12:32
  • @derpirscher Wouldn't this add a div to all the items in the array? I want the first 2 items in a single div because they have the same date and the 3rd item in a separate div. – DragonBorn Nov 25 '22 at 12:34
  • No it wouldn't. It would create a `
    ..
    ` and inside this div it would would create the elements you return from `map` Of course, you shouldn't return a `div` from the `map` ...
    – derpirscher Nov 25 '22 at 12:35
  • And if you want some special grouping, you have to create that beforehand. Ie group your `history` by date first. Then iterate over the groups and create all elements within the group – derpirscher Nov 25 '22 at 12:37
  • First, [group by date](https://stackoverflow.com/questions/46802448/how-do-i-group-items-in-an-array-by-date) and then do the `map` on the resulting array and the nested array inside it. – adiga Nov 25 '22 at 12:39
  • Ca you try to remove the fragments tags ? – Amr Nov 25 '22 at 12:42
  • @adiga Thanks grouping worked – DragonBorn Nov 25 '22 at 13:54

2 Answers2

1

React under hood transforms JSX <div><h2>text</h2</div> into

React.createElement('div', undefined, React.createElement('h2',undefined,'text'))

what are you trying to do is impossible with this architecture.

Instead you need to find some other approach. Maybe group data by date and then render. Something like

function renderHistory () {
const grouped = props.history.reduce((acc,item)=>{
  const dateAdded = item.datetime.split(" ")[0];
   return {...acc, [dateAdded]: [...acc[dateAdded], item]}
  },{});

  return Object.keys(grouped).map((dayAdded) => {
     return (
     <div key={dayAdded}>
        <h2>{dateAdded}</h2>
       { grouped[dayAdded].map((item)=>(
         <li key={item.item+dayAdded}>{item.item} - {item.calories} calories</li>)
         )}
      </div>
    )

  })
}
Wraithy
  • 1,722
  • 1
  • 5
  • 13
  • It says unexpected token on this line `return {...acc, [dateAdded]: ...acc[dateAdded], i}` at the first period here `...acc[dateAdded]` – DragonBorn Nov 25 '22 at 13:08
  • sry forgot braces, should work now – Wraithy Nov 25 '22 at 13:12
  • It should be `acc.datetime.split` right? I changed `item` to `acc` since you are using `acc` instead of `item` but now I am getting `Uncaught TypeError: acc.datetime is undefined` and when I log `acc` it's an empty object. – DragonBorn Nov 25 '22 at 13:28
  • sry, that `i` should be `item`, acc is that object which will be returned.. ill fix it – Wraithy Nov 25 '22 at 13:29
  • `acc[dateAdded] is undefined` – DragonBorn Nov 25 '22 at 13:38
  • I changed the return in your reduce function to this and it worked `if (!acc[date]) {acc[date] = [];}acc[date].push(item);return acc;` – DragonBorn Nov 25 '22 at 13:56
1
import "./styles.css";
const foodItems = [
  { item: "cake", calories: 120, datetime: "2022-11-16 07:51:26" },
  { item: "chicken", calories: 250, datetime: "2022-11-16 13:48:46" },
  { item: "pizza", calories: 420, datetime: "2022-11-25 11:13:42" }
];

const foodItemsGroupedByDate = {};

foodItems.map((foodItem) => {
  const date = foodItem.datetime.split(" ")[0];
  foodItemsGroupedByDate[date] = foodItemsGroupedByDate[date] || [];
  foodItemsGroupedByDate[date].push({
    item: foodItem.item,
    calories: foodItem.calories
  });
});

const foodItemsArray = Object.entries(foodItemsGroupedByDate);

function RenderFoodItems({ items, date }) {
  return (
    <>
      <div>{date}</div>
      {items.map((item) => {
        return <div>{item.item} - {item.calories}</div>;
      })}
    </>
  );
}
export default function App() {
  return (
    <div className="App">
      {foodItemsArray.map((item) => {
        return <RenderFoodItems date={item[0]} items={item[1]} />;
      })}
    </div>
  );
}

Try something like this, tested in codesandbox.

Daniel Smith
  • 179
  • 8