0

I'm creating a menu page using React and Redux. I currently have the entire menu of over 100 items on an array of objects, which includes a 'category' property for the type of food (appetizers, burgers, desserts, etc.)

I originally just mapped through all of the items and rendered them all like this:

render(){
    let foodList = this.props.foodMenu.map((food) => (
        <Food className="food-menu" key={food.id} food={food} addToCart={this.addToCart}/>
    ))

    return (
        <div >
            <h2>Food Menu</h2>
            {foodList}
        </div>
    )

However, I want to be able to separate the menu by categories, which led me to this:

    render(){
    let appetizers = this.props.foodMenu.filter(food => food.category === 'appetizers').map((food) => (
        <Food className="food-menu" key={food.id} food={food} addToCart={this.addToCart}/>
    ))

    let soupsalad = this.props.foodMenu.filter(food => food.category === 'soupsalad').map((food) => (
        <Food className="food-menu" key={food.id} food={food} addToCart={this.addToCart}/>
    ))


    let steaks = this.props.foodMenu.filter(food => food.category === 'steaks').map((food) => (
        <Food className="food-menu" key={food.id} food={food} addToCart={this.addToCart}/>
    ))


    return (
        <div>
            <h2>Food Menu</h2>
            <h3>Appetizers</h3>
            <div className='container'>
                    {appetizers}
            </div>
            <h3>Soup or Salad</h3>
            <div className='container'>
                    {soupsalad}
            </div>
            <h3>Steak</h3>
            <div className='container'>
                    {steaks}
            </div>
        </div>

Except instead of 3 categories, I have 12. As you can see, this starts to become extremely repetitive and not "DRY". I was wondering if there was a cleaner way to go about doing this?

Justin C.
  • 109
  • 1
  • 9
  • Group by then iterate through them. https://stackoverflow.com/questions/14446511/most-efficient-method-to-groupby-on-a-array-of-objects – Train Nov 28 '18 at 23:15
  • If I do a group by in my database and send it back to my state, I would still need to map through each individual category. Is there another way I could do this? – Justin C. Nov 29 '18 at 01:28
  • You could take that answer and turn it into an array of objects that looks like `[{value: "value1", category: [...every item for that category]}, {value: "value2", category: [...every item for that category]}]`. Then you just iterate through that and use something like your original method to create the html. – Train Nov 29 '18 at 14:58

2 Answers2

1

Based on previous answer I decided to make a real working solution of that. I also added another way of doing this with the forEach method. Please do run the snippet to see the results.

Cheers!

const Food = props => {
  //Do something that make sense with this :)
  const { food, addToCart, className } = props;
  return (
    <React.Fragment>
      Food: {food.id} <br />
    </React.Fragment>
  );
};

const App = props => {
  const categories = {
    Appetizers: "appetizers",
    Soupsalad: "soupsalad",
    Steaks: "steaks"
  };

  var menus1 = [];
  Object.keys(categories).forEach(categorie => {
    var subMenus = props.foodMenu
      .filter(food => food.category === categories[categorie])
      .map((food,i) => (
        <div key={i}>
          <h3>{categorie}</h3>
          <div className="container">
            <Food
              className="food-menu"
              key={food.id}
              food={food}
              addToCart={""}
            />
          </div>
        </div>
      ));
    menus1 = [...menus1, subMenus];
  });

  const menus2 = Object.entries(categories).map(e => {
    return props.foodMenu
      .filter(food => food.category === e[1])
      .map((food,i) => (
        <div key={i}>
          <h3>{e[0]}</h3>
          <div className="container">
            <Food
              className="food-menu"
              key={food.id}
              food={food}
              addToCart={""}
            />
          </div>
        </div>
      ));
  });

  return (
    <React.Fragment>
      <h2>Food Menu1 with foreach</h2>
      {menus1}

      <h2>Food Menu2 with map of map</h2>
      {menus2}
    </React.Fragment>
  );
};


var foodMenu = [
  { id: "food1", category: "appetizers" },
  { id: "food2", category: "soupsalad" },
  { id: "food3", category: "steaks" }
];
const rootElement = document.getElementById("root");
ReactDOM.render(<App foodMenu={foodMenu} />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
geostack
  • 26
  • 4
1

@geostack posted a great answer. Here is also something I found worked for my application.

const sortByCategory = (dataArray) => {
    let sortedByCategory = {}
    dataArray.forEach(item => {
        if (sortedByCategory[item.category]) {
            sortedByCategory[item.category].push(item)
        } else {
            sortedByCategory[item.category] = []
            sortedByCategory[item.category].push(item)
        }
    })
    return sortedByCategory
}

render(){
    let sortedData = sortByCategory(this.props.foodMenu)
    let foodMenu = []

    for(let key in sortedData) {
        foodMenu.push(
            <div >
                <h3>{key.toUpperCase()}</h3>
                <br />
                <div>
                    {
                        sortedData[key].map(food => {
                            return (
                                <div key={food.id}>
                                    <Food  key={food.id} food={food} addToCart={this.addToCart}/>
                                    <hr />
                                </div>
                            )
                        })
                    }
                </div>
            </div>
        )
    }

    return (
        <div>
            {foodMenu}
        </div>
    )
}
Justin C.
  • 109
  • 1
  • 9