2

I am creating a menu application using React. My app accepts an array of objects which then need to be mapped into a menu. The structure of the menu is like this:

Title
  Header
    Item
    Item
  Header
    Item
    Item

The data objects I receive are structured like: {title: "Drinks", header: "Beer", item: "Blue Moon"}. I filter the array so I only get objects with the same title. My issue is that I do not know how many different headers are going to come in. I need my mapping function to display each header and all associated items. Currently the title is handled outside of the mapping function because there will only be one title per menu.

        <div className={style.menuItemTitle}>{title}</div>
        {currentMenu.map((item, index) => (
          <div className={style.menuHeader}>{item.header}</div>
          <div className={style.menuItem}>{item.item}</div>
        ))}

The above code lists the header above every single item. I only want each header to display once with all of the associated items below.

tdammon
  • 610
  • 2
  • 13
  • 39
  • `currentMenu.filter(item=>item.header==='beer').map(element=>element.item)` gives list of items – arturasmckwcz Feb 16 '21 at 20:07
  • I don't know what each header is going to be though. – tdammon Feb 16 '21 at 20:09
  • I would suggest using a different data structure, like `{ title: "Drinks", items: [ { header: "Beer", items: [ { name: "Blue Moon" }, { name: "Heineken" } ] }, { header: "Liquor", items: [ { name: "Jameson Irish Whiskey" }, { name: "Stolichnaya Vodka" } ] }` Then it would be easier to map them. – Heretic Monkey Feb 16 '21 at 20:10
  • @tdammon, try this: `const uniqueHeaders=(menu)=>{ let result=[]; for (let item of menu){ if (result.indexOf(item.header)===-1){ result.push(item.header)}; }; return result}` it returns array of unique headers – arturasmckwcz Feb 16 '21 at 20:24
  • That will get me an array of the unique headers but I still need to map everything into the menu. – tdammon Feb 16 '21 at 20:25
  • @tdammon so you map array of unique headers and inside filter items like in my comment above – arturasmckwcz Feb 16 '21 at 20:30

3 Answers3

1
const uniqueHeaders=(menu)=>{
  let result=[];
  for (let item of menu){
    if (result.indexOf(item.header)===-1){
      result.push(item.header)
    };
  };
  return result
}

uniqueHeaders(currentMenu).map(uniqueHeader=>
  currentMenu.filter(item=>
    item.header===uniqueHeader).map(element=>
      element.item))
arturasmckwcz
  • 104
  • 1
  • 7
  • I think this is very close but I am getting the following error still "Objects are not valid as a React child (found: object with keys {title, header, item}). If you meant to render a collection of children, use an array instead." – tdammon Feb 16 '21 at 21:06
  • This code should return an array containing arrays with items for each unique header. Say it was 3 beers and 2 wines, the result should be `[[beer1,beer2,beer3],[wine1,wine2]]` It isn't easy to help you with react error without seeing the code. – arturasmckwcz Feb 16 '21 at 21:16
  • You're most welcome! BTW I'd suggest you use @ANOL GHOSH answer and replace `uniqueHeaders(currentMenu)` with his perfect line of code. – arturasmckwcz Feb 16 '21 at 21:21
1
let array = [1,2,3,4,5,2,4,3];
let unq = array.filter((itm,pos,self)=>{ return self.indexOf(itm) == pos});

Unq = [1, 2, 3, 4, 5]

For nested array

let menu = {"title":"Drinks","items":[{"header":"Beer","items":[{"name":"Blue Moon"},{"name":"Heineken"}]},{"header":"Beer","items":[{"name":"Blue Moon"},{"name":"Heineken"}]},{"header":"Liquor","items":[{"name":"Jameson Irish Whiskey"},{"name":"Stolichnaya Vodka"}]}]}

From https://stackoverflow.com/a/64489112/14499047

const uniq_arr = (x,f)=>Object.values(x.reduce((a,b)=>((a[f(b)]=b),a),{}));
let header = uniq_arr(menu.items,(v)=> v.header);
for(item in header) {
    let header_name  = header[item].header //Header Name
    let lists = header[item].items; // Header List
    for (list in lists) {
        lists[list].name // Names in heaeder list
    }
}

Result

  • Drinks
    • Beer
      • Blue Moon
      • Heineken
    • Liquor
      • Jameson Irish Whiskey
      • Stolichnaya Vodka

let body = $('body');
    window.menu = {"title":"Drinks","items":[{"header":"Beer","items":[{"name":"Blue Moon"},{"name":"Heineken"}]},{"header":"Beer","items":[{"name":"Blue Moon"},{"name":"Heineken"}]},{"header":"Liquor","items":[{"name":"Jameson Irish Whiskey"},{"name":"Stolichnaya Vodka"}]}]};
    const uniq_arr = (x,f)=>Object.values(x.reduce((a,b)=>((a[f(b)]=b),a),{}));
    let header = uniq_arr(menu.items,(v)=> v.header);
    body.append($('<ul/>').append($('<li/>').attr('id','menu').text(menu.title)));
    for(item in header) {
        $('#menu').append($('<ul/>').append($('<li/>').attr('id',header[item].header+'_header').text(header[item].header)));
        let lists = header[item].items;
        for (list in lists) $('#'+header[item].header+'_header').append($('<ul/>').append($('<li/>').text(lists[list].name)));
        
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
ANOL GHOSH
  • 1
  • 1
  • 9
0

Try this snippet, the idea here is to group by the title

let list=[
  {title: "Drinks", header: "header1", item: "Blue Moon"},
  {title: "Drinks", header: "header1", item: "Blue Moon"},
  {title: "Drinks", header: "header2", item: "Blue Moon"}
]

const result = list.reduce((acc, curr) => {
  if(!acc[curr.header]) 
    acc[curr.header] = [];
  
  acc[curr.header].push(curr);
  return acc;
},{});

console.log(result)
/*
Object.keys(res).forEach(key=>{
  let curr=res[key];
      <div className={style.menuItemTitle}>{key}</div>
        {curr.map((item, index) => (
          <div className={style.menuHeader}>{item.header}</div>
          <div className={style.menuItem}>{item.item}</div>
        ))}
})
*/

      
<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>
Abdelrahman Hussien
  • 505
  • 2
  • 4
  • 17