Using a map within a map in jsx - javascript

{normalizedData.map(obj =>
<div key={obj.display_date_numberic}>
<div>{obj.display_date_numberic}</div>
</div>
{!isEmpty(obj.applicants) && obj.map(obj2 =>
<div className="events">{obj2.person.name}</div>
)}
)}
I'm getting an error on the following line:
{!isEmpty(obj.applicants) && obj.map(obj2 =>
Why can't I use the map function inside another map? normalizedData has an array of objects and each obj has another array of objects.

You can do a map within a map as follows:
e.g. given data of
outerArray: [
{ id: '1st', innerArray: [ 'this', 'that' ]},
{ id: '2nd', innerArray: [ 'some', 'what' ]},
]
you can use JSX:
<ul>
{outerArray.map(outerElement => {
return outerElement.innerArray.map(innerElement => (
<li key={outerElement.id}>
{innerElement} - {outerElement.id}
</li>
))
})}
</ul>
or slightly more succinctly:
<ul>
{outerArray.map(outerElement => (
outerElement.innerArray.map(innerElement => (
<li key={outerElement.id}>
{innerElement} - {outerElement.id}
</li>
))
))}
</ul>
which renders:
<ul>
<li>this - 1st</li>
<li>that - 1st</li>
<li>some - 2nd</li>
<li>what - 2nd</li>
</ul>
note the use of key= inside the map to ensure React has a unique reference for the element from each loop iteration. If you don't it will warn you!

Reason is, you are trying to render more than one element, and we can't do that, we can return only one element. So wrap all the logic and elements inside one div like this:
{normalizedData.map(obj =>
<div key={obj.display_date_numberic}>
<div>{obj.display_date_numberic}</div>
{Array.isArray(obj.applicants) && obj.applicants.map(obj2 =>
<div className="events">{obj2.person.name}</div>
)}
</div>
)}
Assuming obj.applicants is an array, use Array.isArray to check whether any value is a proper array or not
Note: We can use map only on array not on any object, so if obj is an object and use Object.keys(obj) to get an array of all the keys then use map on that.

The evident error that you hace in your code is that , you should be mapping on obj.applicants in the inner map and not obj and return a single element from the outer map
Also if obj.applicants is an array, no need to use isEmpty
{normalizedData.map(obj =>
<div>
<div key={obj.display_date_numberic}>
<div>{obj.display_date_numberic}</div>
</div>
{ obj.applicants.map(obj2 =>
<div className="events">{obj2.person.name}</div>
)}
</div>
)}

Just advice, you can use for instead of map

Related

React.js: How to filter JSX element array on custom attribute?

I am starting with a simple array of JSX elements:
const jsxArray = dataItems.map(item => (
<div>
<Header>{item.title}</Header>
<Paragraph>{item.body}</Paragraph>
<Paragraph customAttribute={item.isActive} >{item.tags}</Paragraph>
</div>
))
Inside render, or rather return since I use functional components for everything now, I'd like to filter for JSX elements where the isActive attribute was tagged true.
return (
{jsxArray
.filter(jsxElement => // want to filter in JSX elements
// that are true for customAttribute keyed to `item.isActive`)
}
)
Is there any way to do it?
If there is not precisely a good way I am open to workarounds.
It is possible for me to simply filter the array at an earlier step. It would result in some extra code duplication though, since I would still need the array of unfiltered JSX elements elsewhere.
You don't filter the list after you render it. At that point it's just a tree of nodes that doesn't have much meaning anymore.
Instead you filter the items first, and then render only the items that pass your criteria.
const jsxArray = dataItems.filter(item => item.isActive).map(item => (
<div>
<h3>{item.title}</p>
<p>{item.body}</p>
<p customAttribute={item.isActive} >{item.tags}</p>
</div>
))
It is possible for me to simply filter the array at an earlier step. It would result in some extra code duplication though, since I would still need the array of unfiltered JSX elements elsewhere.
Not necessarily. When dealing with filtering like this myself I create two variables, one for the raw unfiltered list and one for the filtered items. Then whatever you're rendering can choose one or the other depending on its needs.
const [items, setItems] = useState([])
const filteredItems = items.filter(item => item.isActive)
return <>
<p>Total Items: ${items.length}</p>
<ItemList items={filteredItems} />
</>
Instead of accessing the jsx element properties (which I think it's either not possible or very difficult) I suggest you to act in this way:
Save the renderer function for items in an arrow function
const itemRenderer = item => (
<div>
<Header>{item.title}</Header>
<Paragraph>{item.body}</Paragraph>
<Paragraph customAttribute={item.isActive} >{item.tags}</Paragraph>
</div>
)
Save the filter function in an arrow function
const activeItems = item => item.isActive
Use them to filter and map
const jsxArray = dataItems.filter(activeItems).map(itemRenderer)
Use them to map only
const jsxArray = dataItems.filter(activeItems).map(itemRenderer)
Hope this helps!
Usually you would filter the plain data first and then render only the markup for the filtered elements as described in #Alex Wayne answer.
If you worry about duplication of the markup, that can be solved by extracting a component from it:
const Item = ({title, body, isActive, tags}) => (
<div>
<Header>{title}</Header>
<Paragraph>{body}</Paragraph>
<Paragraph customAttribute={isActive}>{tags}</Paragraph>
</div>
);
For rendering the filtered list you can then do:
{items.filter(item => item.isActive).map(item => <Item {...item} />)}
and for the unfiltered list:
{items.map(item => <Item {...item} />)}

Using Array.map in React

I'm confused how this code work
const sidebar = (
<ul>
{
props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)
}
</ul>
);
I know that a map returns an array, but how can it return all these tags
<li key={post.id}>
{post.title}
</li>
and render it?
I have tried changing it to forEach but it's not working. Nothing displays on the UI:
const sidebar = (
<ul>
{
props.posts.forEach((post) =>{
return <li key={post.id}>
{post.title}
</li>
})
}
</ul>
);
The map() method creates a new array with the results of calling a
provided function on every element in the calling array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
The forEach() method executes a provided function once for each
array element.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
This means if you try to return something in the method executed in forEach you are not creating an array of tags.
JSX is expecting an array of tags to be rendered but in forEach you are not creating an array you are just iterating over the array so JSX receives nothing at the end

Using map to iterate through two arrays

Currently in React, I am using array.map(function(text,index){}) to iterate through an array. But, how am I going to iterate through two arrays simultaneously using map?
EDIT
var sentenceList = sentences.map(function(text,index){
return <ListGroupItem key={index}>{text}</ListGroupItem>;
})
return (
<div>
<ListGroup>
{sentenceList}
</ListGrouup>
</div>
);
Like, in this I want icons to be prepended with every iteration. And I'm planning to have those icons in another array. So, thats why iterate two arrays.
If at all possible, I would recommend storing the text alongside the images in an array of objects, eg:
const objects = [{text: 'abc', image: '/img.png' }, /* others */];
that way you can just iterate through the array and select both members at the same time, for example:
objects.map(item => (<Component icon={item.image} text={item.text} />) )
If this isn't possible then just map over one array and access the second array's members via the current index:
sentences.map((text, index) => {
const image = images[index];
return (<Component icon={image} text={text} />);
});
Are the both arrays of same length? You can do something like below if your intention is to combine both in some way.
array.map(function(text,index){
return text + ' ' + array2[index]
})
In your case:
var sentenceList = sentences.map(function(text,index){
return <ListGroupItem key={index}>{text} <img src={icons[index]} /i> </ListGroupItem>;
})
return (
<div>
<ListGroup>
{sentenceList}
</ListGrouup>
</div>
);
Notice, How Icon src is being assigned. The idea is that access icons array with the same index to get a corresponding icon.
You can't do this with built-in Array.prototype methods, but you can use something like this:
function map2(arr1, arr2, func) {
return arr1.map(
(el, i) => { return func(el, arr2[i]); }
);
}
(Of course, arr1 and arr2 are expected to have the same length)
Generally, what you're looking for is a zip function, such as the one that lodash provides. Much like a real zipper, it combines two things of the same length into one:
const zipped = _.zip(sentences, icons);
return (
<div>
<ListGroup>
{zipped.map(([sentence, icon], index) => (
<ListGroupItem key={index}><Icon icon={icon} /> {text}</ListGroupItem>;
))}
</ListGroup>
</div>
);
Note, this is doing more iterations than are technically needed. If performance is an issue, you may want a solution that's a bit smart (not really in scope for your question though).

List elements not rendering in React [duplicate]

This question already has answers here:
When should I use a return statement in ES6 arrow functions
(6 answers)
Closed 26 days ago.
This is my Sidebar component.
const Sidebar = React.createClass({
render(){
let props = this.props;
return(
<div className= 'row'>
<h2>All Rooms</h2>
<ul>
{props.rooms.map((room, i) => {
<li key={i}> {room.name} </li>
})}
</ul>
{props.addingRoom && <input ref='add' />}
</div>
);
}
})
This is where I render it populating one room.
ReactDOM.render(<App>
<Sidebar rooms={[ { name: 'First Room'} ]} addingRoom={true} />
</App>, document.getElementById('root'));
The contents inside the <ul></ul> tag don't render at all. Any idea what I could be missing.
You aren't returning anything from the map function, so it renders nothing inside the unordered list. In your arrow function, you do:
(room, i) => {
//statements
}
This doesn't return anything. Array.prototype.map requires a callback that returns a value to do anything. map transform elements to the array, mapping (executing) the callback on each element. The return value is what the value is in the corresponding position in the returned and mapped array.
You must return the list element in the map function like so:
<ul>
{props.rooms.map((room, i) => {
return <li key={i}> {room.name} </li>;
})}
</ul>
Now, the mapped array of list elements is rendered because the list element is returned.
To make this shorter, you may write it in the form (params) => value which is equivalent to the above, where value is the returned value, like so:
{props.room.map((room, i) => <li key={i}> {room.name} </li>)}

Create html structure which will allow only 3 div elements in each li. In React + underscore.js

This is bit copy of How to create html structure which will allow only 3 div elements in each li. In React + underscore.js
Actually i am facing issue while iterating array in li.
I have below array structure
var xyz = [{'name': ['abc','xyz','test','test1','test2','test3','test4'] }];
and i want below output structure
<ul>
<li>
<div><span>test3</span></div>
<div><span>test4</span></div>
<div><span>test5</span></div>
</li>
<li>
<div><span>test6</span></div>
<div><span>test7</span></div>
<div><span>test8</span></div>
</li>
I am using React and underscore.js on my project.
I want everything in pure react function I tried to append li's into ul as a string but that is bad practice it react.js
Can anyone help me in this.
Using below function i have created array structure which will contain 3 values in one array object.
var n = 3;
var chunkedlists = _.chain(this.state.serialIdFilter).groupBy(function(element, index){
return Math.floor(index/n);
}).toArray().value();
Now i want help in printing this into proper structure as i mentioned using react + underscore.js.
const Com = () => {
var xyz = [{'name': ['abc','xyz','test','test1','test2','test3','test4'] }];
return (
<ul>
{_.map(_.chunk(xyz[0].name, 3), (innerItem, i) => (
<li key={i}>
{_.map(innerItem, (name, j) => (<div key={j}><span>{name}</span></div>))}
</li>
))}
</ul>
);
}
ReactDOM.render(<Com />, document.getElementById("container"));
An example bin an be found here:
http://jsbin.com/menidu/4/edit?js,output
something like this would be required
return (
<ul>{chunkedLists.map(group => <li>{group.map(i => <div><span>{i}</span></div>)}</ul>)}
)

Categories

Resources