Display and Map Multiple Keys and its object data - javascript

I have a JSON file which I need to map and display but I am not sure how to map the structure like this as this is a bit different.
Below is my JSON file:
var data = {
"megamenu": [
{
"name": "level1.2",
"link": "#",
"multilevel": {
"A": [
{
"name": "A-one",
"link": "#"
}
],
"B": [
{
"name": "B-one",
"link": "#"
}
]
}
},
]
}
Map Function:
{data.megamenu.map((menuitem, index) => (
<li key={index}>{Object.entries(menuitem.multilevel).length}</li>
))}
Following is the sample https://stackblitz.com/edit/react-8in6yq
I want to create a structure something like this:
<ul>
<li>level1.2
<ul>
<li>multilevel
<ul>
<li>A
<ul>
<li>A-one</li>
</ul>
</li>
<li>B
<ul>
<li>B-one</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
Note: I know I can change the structure to object and not array but this is something I cannot handle and change. So have to manage with this structure.

So why can't you do something like:
const Multilevel = ({multilevel}) => (
<ul>
{Object.keys(multilevel).map(key => (<li key={key}>{key} /* etc*/</li>))}
</ul>
);
{data.megamenu.map((menuitem, index) => (
<li key={menuitem.name}>
{menuitem.name}
<ul>
<li>multilevel
<Multilevel multilevel={menuitem.multilevel}/>
</li>
</ul>
</li>
))}
That's a bit complex, and I advice you to separate your lists to different components to make code readable.

You need to loop through inner multilevels
{data.megamenu.map((menuitem, index) => (
const innnerItemsDOM = menuitem.multilevel.map((item, index) =>{
return <li>
<ul>
<li>item.name</li>
</ul>
</li>
})
return <li> level1.2
{innnerItemsDOM}
</li>
))}

Related

How to map a nested object

I have to populate the li element with data stored as a JSON object.
With "title" it works simple. But it's not when talking about name's
values. How can I map the subMenu object to get the name?
<ul>
{data.map(({ title, subMenu }) => (
<li className="mobileMenu-body-nav-item">
<button className="mobileMenu-body-nav-item-btn">
*** here I have to put name ***
</button>
</li>
))}
</ul>
JSON object
[
{
"title": "Breeds",
"subMenu": [
{
"id": 1,
"name": "Dog Breeds"
},
{
"id": 2,
"name": "Cat Breeds"
}
]
},
{
"title": "About Pet Adoption",
"subMenu": [
{
"id": 3,
"name": "About Dog Adoption"
},
{
"id": 4,
"name": "About Cat Adoption"
}
]
}
]
As noted in the comments on the accepted answer div elements are not valid children of buttons (related question) and while one should assign a key to mapped elements in React using the index of the iterated array is not always ideal. (see the Docs or related article from T.J.Crowder's comment).
Given that you are mapping a nested list it seems more appropriate to structure it as such. Here using the title as the outer li key (though a more definite unique property would be better) and the subMenu.id as a key for the inner li.
<ul>
{data.map(({ title, subMenu }) => (
<li key={title} className='mobileMenu-body-nav-item'>
<ul>
{subMenu.map(({ id, name }) => (
<li key={id}>
<button className='mobileMenu-body-nav-item-btn'>{name}</button>
</li>
))}
</ul>
</li>
))}
</ul>
You can just call map again, like this:
<ul>
{data.map(({ title, subMenu }) => (
<li className="mobileMenu-body-nav-item">
<button className="mobileMenu-body-nav-item-btn">
{subMenu.map(({ name }) => (<span>{name}</span>))}
</button>
</li>
))}
</ul>
Change the <span> tag to match however you want this content to be rendered.
Also, if this is React, don't forget to set the key prop appropriately when using map:
<ul>
{data.map(({ title, subMenu }) => (
<li key={title} className="mobileMenu-body-nav-item">
<button className="mobileMenu-body-nav-item-btn">
{subMenu.map(({ name }) => (<div key={name}>{name}</div>))}
</button>
</li>
))}
</ul>

Loop inside JSX

I have an object similar to this:
{
id: number,
kids: [{
id: number,
kids: [{
id: number,
kids: []
}]
}]
}
So it has property kids which is an array of kids each of which might have its own array of kids. I need to render original object in a tree view list like this:
<ul>
{object.map(item => (
<li>
<p>{item.value}</p>
{item.kids ?
{item.kids.map(item => (
<ul>
<li>
<p>{item.value}</p>
</li>
</ul>
))}
: null
}
</li>
))}
</ul>
So every item of kids will be a <li></li> element with <ul></ul> inside of it if item.kids isn't empty array.
I could keep going like this but it's pretty messy and more importantly I don't know when exactly kids property will be an empty array. So I need somehow loop over original object and it's properties and do something like
while (kid.kids) {
return {kid.kids.map(kid => (
<li>
<p>{kid.value}</p>
<ul>
kid.kids.map(kid => (
etc
))
</ul>
</li>
}))
}
But I can't understand the better way to loop like this.
This is probably best solved with recursion.
const Kids = ({id, kids}) => {
return {
<li key={id}>
<p>{id}</p>
{kids
? (<ul>{kids.map((kid) => <Kids id={kid.id} kids={kid.kids} />)}</ul>)
: null;
}
</li>
}
};

How to call the recursive call at last if it meets the condition?

I have nested object called option. It has nested (potentially infinite) objects called childOptions key.
option = {
name: 'o1', label: 'o1', childOptions: [
{ name: 'o4', label: 'o4', childOptions: [
{name: 'o5', label: 'o5',
]},
{ name: 'o2', label: 'o3'},
{ name: 'o3', label: 'o3'}
]
}
This is my render function to render JSX based on that.
function renderOption(option): JSX.Element {
if (option.childOptions) {
return (
<li
key={option.name}
>
<span>
{option.label}
</span>
<ul>
{option.childOptions.map(renderOption} << recursive call!
</ul>
</li>
);
}
I have nested option object which passes into renderOption
This is the current output (CORRECT But not what I want)
<li>
<span>o1</span>
<ul>
<li>
<span>o4</span>
<ul>
<li><span>o5</span></li>
<ul>
</li>
<li><span>o2</span></li>
<li><span>o3</span></li>
</ul>
</li>
Because we show o2 and o5 right after o1, user gets confused to read.
This is what I need
<li>
<span>o1</span>
<ul>
<li><span>o2</span></li>
<li><span>o3</span></li>
<li>
<span>o4</span>
<ul>
<li><span>o5</span></li>
<ul>
<li>
</ul>
</li>
I think it might be not possible. How do we call the recursive call at last if it meets the condition?
I still want to keep the recursive call if we could.
Sort the childOptions in your object by name:
function renderOption(option): JSX.Element {
if (option.childOptions) {
option.childOptions.sort((a, b) => a.name.localeCompare(b.name));
return (
<li
key={option.name}
>
<span>
{option.label}
</span>
<ul>
{option.childOptions.map(renderOption} << recursive call!
</ul>
</li>
);
}
You could return a ul for an array of options. Get the array of options as props to the function component. Pass the initial option object as an array to make it uniform and easier to make a recursive call
Here's a runnable snippet:
const option={name:"o1",label:"o1",childOptions:[{name:"o4",label:"o4",childOptions:[{name:"o5",label:"o5"}]},{name:"o2",label:"o2"},{name:"o3",label:"o3"}]};
function Display({ options }) {
return (
<ul>
{options.map(c => (
<li key={c.name}>
<span>{c.label}</span>
{c.childOptions && <Display options={c.childOptions} />}
</li>
))}
</ul>
);
}
ReactDOM.render(
<Display options={[option]} />,
document.getElementById("root")
);
<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"></div>

React JS - data.map can't create list with the data

I got data.js file with some basic info for food like:
export default [
{
"name": "Crock Pot Roast",
"information":[
{
"date":"24 July 2019",
"type": "Main dish",
"difficulty": "Easy",
"time": "~50",
}
],
"ingredients": [
{
"quantity": "1",
"name": " beef roast",
"type": "Meat"
}
],
...
Some more data
...
}
]
I want to create list on react that can get all elements from data.information which function will be correct to use?
I'm getting the data like this:
const getData = data.map(food => {
return (
<div key={food.name}>
<span>{food.name}</span>
<div>
<div>
<img src={food.imageURL} alt={food.name} />
<div>
<ul>{getFoodInformation}</ul>
</div>
</div>
</div>
<div>food ingredients</div>
<div>food recipe</div>
</div>
);
});
but I can't use food.information.map to create list like:
<ul>{food.information.map((info) => <div key={info.date}>{info}</div>) }</ul>
Any ideas maybe to use another function not map?
You're right that this line is the problem:
<ul>{food.information.map((info) => <div key={info.date}>{info}</div>) }</ul>
But, .map() is not a problem. Your information data is an array, so you can .map() it with JSX.
Your problem is this:
<div key={info.date}>{info}</div>
info is an object, and React doesn't know how to print an object "beautifully". You want to use the string data (for example, {info.type}).
If you need to dump the full object, use JSON.stringify like this:
<div key={info.date}>{JSON.stringify(info)}</div>
React does not render array/object into a list by default. you have to be explicit in your code.
your first attempt is 'close' to correct, however, you can notice, ingredients and information are both arrays of objects. it means that you have to generate a tag for each index.
I used JSON.stringify to generate a unique key since using an index for key is bad practice.
The code below shows you one of the correct approaches using react.
const data = [
{
name: "Crock Pot Roast",
imageURL: "https://via.placeholder.com/150",
information: [
{
date: "24 July 2019",
type: "Main dish",
difficulty: "Easy",
time: "~50"
}
],
ingredients: [
{
quantity: "1",
name: " beef roast",
type: "Meat"
}
]
}
];
const CardRecipe = ({data}) => {
const { name, imageURL, information, ingredients } = data;
return (
<div key={name}>
<span>{name}</span>
<div>
<div>
<img src={imageURL} alt={name} />
<div>
{information &&
information.map(info => {
const { date, type, difficulty, time } = info;
return (
<ul key={JSON.stringify(info)}>
<li> {date} </li>
<li> {type} </li>
<li> {difficulty} </li>
<li> {time} </li>
</ul>
);
})}
</div>
<div>
{ingredients &&
ingredients.map(ingredient => {
const { quantity, name, type } = ingredient;
return (
<ul key={JSON.stringify(ingredient)}>
<li> {quantity} </li>
<li> {name} </li>
<li> {type} </li>
</ul>
);
})}
</div>
</div>
</div>
<div>food ingredients</div>
<div>food recipe</div>
</div>
);
};
export default function App() {
const first_reciepe = data[0];
return (
<div className="App">
<CardRecipe data={first_reciepe} />
</div>
);
}
Alternatively, you can print any lists by mapping once on the array, and then on each object/key to generate (x) amount of <"li"> tags.
Example:
const DoList = ({ data }) => {
return data.map(list => {
return (
<ul key={list}>
{Object.keys(list).map(key => {
const value = list[key];
return <li key={key}> {value} </li>;
})}
</ul>
);
});
};
Then you would use it as many time as you want like this:
<DoList data={ingredients} />
<DoList data={information} />
<DoList data={...} />

Typescript .push'ing data from a nested array

I am having issues returning (pushing) data from a nest array. The JSON object I have is as follows:
{
component: Assembly,
title: "Assembly",
path: "/assembly",
sections: {
title: "Frame Assembly",
steps: {
["Step 1"]: {
content:"step 1 content"
},
["Step 2"]: {
content: "step 2 content"
}
},
},
}
I am using this data to build a navigation of sorts. This is my function to do this.
private BuildNavigation = (navItem: any) => {
const subSections = [];
const sections = navItem.sections;
for (const key in sections) {
if (sections.hasOwnProperty(key)) {
subSections.push(
<>
<ul>
<li key={key}><ScrollLink to={`${currentPath}#${key}`.toLowerCase()}>{sections[key].title}</ScrollLink></li>
{Object.getOwnPropertyNames(sections[key].steps).forEach((step: string) => {
// How do I return step
console.log(step);
})}
</ul>
</>
)
}
}
return subSections;
}
I can log out step fine and get the correct info but I cannot get it to return (push) into subSections.
I am hoping for some output similar to:
<ul>
<li>
Assembly
<ul style="list-style-type: none; padding: 0px 0px 0px 10px;">
<li>Frame Assembly</li>
<ul>
<li>Step 1</li>
<li>Step 2</li>
</ul>
</ul>
</li>
</ul>
Any help is welcome.
After the line:
<li key={key}><ScrollLink to={`${currentPath}#${key}`.toLowerCase()}>{sections[key].title}</ScrollLink></li>
You can continue as follows:
<ul>
{
Object.getOwnPropertyNames(sections[key].steps).map((step: string) => {
return (<li>{step}</li>);
})
}
</ul>

Categories

Resources