Map over nested array of objects - javascript

I am trying to map over array of objects which each array contains another nested array of objects. However, the map does not work on the nested array. How do i map over the contents of the nested array?
data structure for nested array of objects looks like this:
const items = [
{
page_num: 1,
status: "PROCESSED",
table_text_data:[
{bottom:58, height:60, left:60, right:67, text:"recorded"},
{bottom:75, height:67, left:50, right:60, text:"symbolic"},
{bottom:80, height:80, left:77, right:89, text:"fever"},
]
}
];
map for page_num and status looks like this:
{this.props.items.map(item =>{
return (
<ul><li> page_num={item.page_num} </li>
<li> status:{item.status}</li>
{item.table_text_data.map((c,i)=>(
<ul><li>bottom={c.bottom}</li>
<li>height={c.height}</li>
<li>left={c.right}</li>
<li>right={c.left}</li>
<li>text={c.text}</li></ul>
))}
</ul>
)})}
page_num and status works fine but not for table_text_data. how should i map through it?
screenshot of the warning i'm getting as well:
https://i.stack.imgur.com/sqREQ.png
Any help would be much appreciated. Thank you

Why don't you use re-use array map?
{this.props.items.map((item, i) =>{
return (
<ul key={'parent' + i}><li> page_num={item.page_num} </li>
<li> status:{item.status}</li>
{item.table_text_data.map((c,j)=>(
<li key={'child' + j}>
<ul><li>bottom={c.bottom}</li>
<li>height={c.height}</li>
<li>left={c.right}</li>
<li>right={c.left}</li>
<li>text={c.text}</li></ul>
</li>
))}
</ul>
)})}

the error you give is has the answer in it.
Each child in a list should have a unique key prop
you're already halfway there by extracting the index that holds the nested object.
Now you only have to add the property key={I} to your ul
const items = [
{
page_num: 1,
status: "PROCESSED",
table_text_data:[
{bottom:58, height:60, left:60, right:67, text:"recorded"},
{bottom:75, height:67, left:50, right:60, text:"symbolic"},
{bottom:80, height:80, left:77, right:89, text:"fever"},
]
},
{
page_num: 2,
status: "PROCESSED",
table_text_data:[
{bottom:58, height:60, left:60, right:67, text:"recorded"},
{bottom:75, height:67, left:50, right:60, text:"symbolic"},
{bottom:80, height:80, left:77, right:89, text:"fever"},
]
}
];
const ItemDisplay = (props) => (
<React.Fragment>
{props.items.map(item => (
<ul key={item.page_num}>
<li>page_num={item.page_num}</li>
<li>status={item.status}</li>
{item.table_text_data.map((c,i) => (
<ul key={i}>
<li>bottom={c.bottom}</li>
<li>height={c.height}</li>
<li>left={c.right}</li>
<li>right={c.left}</li>
<li>text={c.text}</li>
</ul>
))}
</ul>
))}
</React.Fragment>
);
ReactDOM.render(
<ItemDisplay items={items} />,
document.getElementById("react")
);
<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="react"></div>

you can use nested map function to acces the files. but you need to speciy that nested map() should work only for table_text_data property,other wise your parent map function will check for the contents inside table_text_data
you can rewrite the code like
{this.props.items.map(item =>{
return(
<ul>
<li> page_num={item.page_num} </li>
<li> status:{item.status}</li>
</ul>
if(item ==='table_text_data'){
{item.table_text_data.map((c,i)=>(
return (
<ul><li>bottom={c.bottom}</li>
<li>height={c.height}</li>
<li>left={c.right}</li>
<li>right={c.left}</li>
<li>text={c.text}</li></ul>
))
}
)}
</ul>
)})}

Related

Each child in a list should have a unique "key" prop. All list objects have unique id but I still get an error [duplicate]

This question already has answers here:
Understanding unique keys for array children in React.js
(27 answers)
Closed last year.
I have some objects in data.json file looking like this:
[{
"id": "1",
"srcImg": "https://upload.wikimedia.org/wikipedia/en/5/5a/Wearethechampions.jpg",
"name": "We are the champions"
}]
All id inside of json are unique.
There is my react code:
function MusicLi({ item }) { //we take item from map() in MusicList function
return (
<div>
<li key={item.id}>
<img src={item.srcImg}/>
<p>{item.name}</p>
</li>
</div>
);
}
function MusicList() {
return (
<div>
{data.map((item) => ( //data - imported json
<MusicLi item={item}/> // send item object to MusicLi function
))}
</div>
);
}
Try this:
function MusicLi({ item }) { //we take item from map() in MusicList function
return (
<div key={item.id}>
<li>
<img src={item.srcImg}/>
<p>{item.name}</p>
</li>
</div>
);
}
Move the key to your top element.
You will required to update the code like
function MusicLi({ item }) {
return (
<div key={`sample_${item.id}`}>
<li key={item.id}>
<img src={item.srcImg}/>
<p>{item.name}</p>
</li>
</div>
);
}
You can define every element unique key.

Could there have been a better way of coding this? (Beginner) React.js [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
This is what I've been taught how to map through arrays with lists. I was wondering if there would be a better way to improve this by keeping it DRY.
I know I could make a single array with objects, however, it's for 3 different columns on a page.
If I were to make changes in the future and maintain the list, would this be sufficient enough?
const groceryList = [
{ Dairy: "Eggs"},
{ Dairy: "Milk"},
{ Dairy: "Cheese"},
];
const shoppingList = [
{ toBuy: "Soap"},
{ toBuy: "Garbage bags"},
];
const getHomeDepot = [
{ getTools: "Plywood"},
{ getTools: "Nails"},
]
{groceryList &&
groceryList.map((ingredients) => {
return (
<ul>
<li>
{ingredients.Dairy}
</li>
</ul>
);
{shoppingList &&
shoppingList.map((items) => {
return (
<ul>
<li>
{items.ToBuy}
</li>
</ul>
);
{getHomeDepot &&
getHomeDepot.map((tools) => {
return (
<ul>
<li>
{tools.getTools}
</li>
</ul>
);
I would make the list into an object, and the different lists into keys. This way, the lists will be easy to add to as they are arrays and you can add list types as keys to the object as needed.
const lists = {
dairy: ["Eggs", "Milk", "Cheese"],
buy: ["Soap", "Garbage Bags"],
tools: ["Plywood", "Nails"]
};
As far as populating the columns... there should only be 1 <ul> surrounding the list items, so this should be outside of the map method. To be dry, you can make a method like this:
const populateListItems = (list) => {
return list.map((listItem) => (
<li>{listItem}</li>
))
};
and then use it to populate your column list items like so:
<div class="col-1">
{list.dairy.length && (
<ul>
{populateListItems(list.dairy)}
</ul>
)}
</div>
<div class="col-2">
{list.buy.length && (
<ul>
{populateListItems(list.buy)}
</ul>
)}
</div>
<div class="col-3">
{list.tools.length && (
<ul>
{populateListItems(list.tools)}
</ul>
)}
</div>
To make this code even more concise, you can use Object.keys to create as many columns as you need dependent on how many list keys are in the "list" object. This will return the same thing as above.
Object.keys(list).map((key, index) => (
<div class=`col-${index}`>
<ul>
{populateListItems(list[key])}
</ul>
</div>
));
I would take a cue from React-Native's FlatList component. At a minimum you want a component that can render an array of items using a renderItem function and can set mapped keys.
const FlatList = ({
data = [],
renderItem,
getKey = (_, i) => i // <-- return array index by default
}) =>
data.map((el, index) => (
<React.Fragment key={getKey(el, index)}>
{renderItem(el, index)}
</React.Fragment>
));
const groceryList = [{ Dairy: "Eggs" }, { Dairy: "Milk" }, { Dairy: "Cheese" }];
const shoppingList = [{ toBuy: "Soap" }, { toBuy: "Garbage bags" }];
const getHomeDepot = [{ getTools: "Plywood" }, { getTools: "Nails" }];
const primitives = ['Bill', 'Ted', 'Socrates'];
const FlatList = ({ data = [], renderItem, getKey = (_, i) => i }) =>
data.map((el, index) => (
<React.Fragment key={getKey(el, index)}>
{renderItem(el, index)}
</React.Fragment>
));
function App() {
return (
<div>
<FlatList
data={groceryList}
renderItem={(el) => <div>{el.Dairy}</div>}
/>
<ul>
<FlatList
data={shoppingList}
renderItem={(el) => <li>{el.toBuy}</li>}
/>
</ul>
<ol>
<FlatList
data={getHomeDepot}
getKey={(el, i) => el.getTools}
renderItem={(el) => <li>{el.getTools}</li>}
/>
</ol>
<ul>
<FlatList
data={primitives}
renderItem={(name, i) => <li>{i} : {name}</li>}
/>
</ul>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/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>

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>

Don't output same items from array

I'm using react js. I try to map an array with objects and to output the name of each object from array:
const arr = [
{
name:"Bill",
age:88
},
{
name:"Bill",
age:18
},
{
name:"Jack",
age:55
},
]
{arr.map(i => (
<li key={i.id}>
{i.name}
</li>
))}
I want to avoid the same name when i do {i.name}. For this i made this:
{new Set(i.name)}
.. but it does not help. How to avoid displaying the same name in map function?
You need to create a Set of names before mapping and rendering. You can do it like below
{[...new Set(arr.map(i => i.name))].map(i => (
<li key={i.id}>
{i.name}
</li>
))}
you can use lodash to overcome this issue. Following code snippet will easily do what you need.
_.uniqBy(arr,"name").map(i => (
<li key={i.id}>
{i.name}
</li>
))}
You can remove the duplicates from the arr based on the name property in each object and then use array map() method on it like:
{[...new Map(arr.map(i=> [i.name, i])).values()].map(i => (
<li key={i.id}>
{i.name}
</li>
))}
Demo:
const arr = [{name:"Bill",age:88},{name:"Bill",age:18},{name:"Jack",age:55}];
class App extends React.Component {
render() {
return (<div>
{[...new Map(arr.map(i=> [i.name, i])).values()].map(i => (
<li key={i.id}>
{i.name}
</li>
))}
</div>);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
<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="app"></div>

How can I map through an object in ReactJS?

I have a response like this:
I want to display the name of each object inside this HTML:
{subjects.map((item, i) => (
<li className="travelcompany-input" key={i}>
<span className="input-label">{ item.name }</span>
</li>
))}
But it throws an error of subjects.map is not a function.
First, I have to define the keys of the objects where it creates an array of keys, where I want to loop through and show the subject.names.
What I also tried is this:
{Object.keys(subjects).map((item, i) => (
<li className="travelcompany-input" key={i}>
<span className="input-label">key: {i} Name: {subjects[i]}</span>
</li>
))}
When calling Object.keys it returns a array of the object's keys.
Object.keys({ test: '', test2: ''}) // ['test', 'test2']
When you call Array#map the function you pass will give you 2 arguments;
the item in the array,
the index of the item.
When you want to get the data, you need to use item (or in the example below keyName) instead of i
{Object.keys(subjects).map((keyName, i) => (
<li className="travelcompany-input" key={i}>
<span className="input-label">key: {i} Name: {subjects[keyName]}</span>
</li>
))}
You get this error because your variable subjects is an Object not Array, you can use map() only for Array.
In case of mapping object you can do this:
{
Object.keys(subjects).map((item, i) => (
<li className="travelcompany-input" key={i}>
<span className="input-label">{ subjects[item].name }</span>
</li>
))
}
Use Object.entries() function.
Object.entries(object) return:
[
[key, value],
[key, value],
...
]
see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
{Object.entries(subjects).map(([key, subject], i) => (
<li className="travelcompany-input" key={i}>
<span className="input-label">key: {i} Name: {subject.name}</span>
</li>
))}
Map over the keys of the object using Object.keys():
{Object.keys(yourObject).map(function(key) {
return <div>Key: {key}, Value: {yourObject[key]}</div>;
})}
I use the below Object.entries to easily output the key and the value:
{Object.entries(someObject).map(([key, val], i) => (
<p key={i}>
{key}: {val}
</p>
))}
Do you get an error when you try to map through the object keys, or does it throw something else.
Also note when you want to map through the keys you make sure to refer to the object keys correctly. Just like this:
{ Object.keys(subjects).map((item, i) => (
<li className="travelcompany-input" key={i}>
<span className="input-label">key: {i} Name: {subjects[item]}</span>
</li>
))}
You need to use {subjects[item]} instead of {subjects[i]} because it refers to the keys of the object. If you look for subjects[i] you will get undefined.
I am not sure why Aleksey Potapov marked the answer for deletion but it did solve my problem.
Using Object.keys(subjects).map gave me an array of strings containing the name of each object, while Object.entries(subjects).map gave me an array with all data inside witch it's what I wanted being able to do this:
const dataInfected = Object.entries(dataDay).map((day, i) => {
console.log(day[1].confirmed);
});
I hope it helps the owner of the post or someone else passing by.
Also you can use Lodash to direct convert object to array:
_.toArray({0:{a:4},1:{a:6},2:{a:5}})
[{a:4},{a:6},{a:5}]
In your case:
_.toArray(subjects).map((subject, i) => (
<li className="travelcompany-input" key={i}>
<span className="input-label">Name: {subject[name]}</span>
</li>
))}

Categories

Resources