I have this array that has this structure please check the code down below , my end results should look like the following :
veg
apple
carrot
meat
chicken
steak
my current results are
apple
carrot
chicken
steak
since I cannot structure the array other wise and don't want to go in to deep nesting or even nest loops which I doubt it will work in react any idea how to achieve the previous results using map , where I map through group only once to create the group name and to then add the items related to that group inside that group?, food for thought : could conditional rendering be also leveraged here ?
I was able to only get either the group multiple times or the items only..
const arr = {
itmes: [
{ id: 1, group: "veg", item: "apple" },
{ id: 2, group: "veg", item: "carrot" },
{ id: 3, group: "meat", item: "chicken" },
{ id: 4, group: "meat", item: "steak" }
]
};
function App() {
return (
<div>
{arr["itmes"].map(
(item) => item.group
//item.item
)}
</div>
);
}
Codesanadbox
You should wrap the items first and render the grouped ones
const groupItems = items =>
items.reduce((groupedItems, item) => {
if (!groupedItems[item.group]) {
groupedItems[item.group] = []
}
groupedItems[item.group].push(item)
return groupedItems
}, {})
const items = Object.entries(groupItems(arr.items)).map(
([groupName, items]) => (
<React.Fragment>
<li>{groupName}</li>
{items.map(item => (
<li>{item.item}</li>
))}
</React.Fragment>
)
)
Option 1
First, make sure your array is sorted by Group:
const sorted = arr["itmes"]
.sort((a, b) => (a.group || '').localeCompare(b.group));
Then you can render and conditionally add another heading element whenever the group name changes:
<ul>
{data.map((d, id) => (
<>
((id > 0 || d.group !== data[id - 1].group) ? <li key={`${id}-h`}><b>{d.group}</b></li> : undefined)
<li key={`${id}-v`}>{d.item}</li>
</>
))}
</ul>
Extra: Custom group sorting
If you need to custom sort the array according to another array:
const sortLtu = ['veg', 'apple', 'meat'];
data.sort((a, b) => sortLtu.indexOf(a.group) - sortLtu.indexOf(b.group));
Option 2: Util function
If you end u doing this often you may create a util function:
Array.prototype.groupBy = function(cb) {
const groups = [];
this.forEach((d, id, arr) => {
const g = cb(d, id, arr);
let group = groups.find(_g => _g.group === g);
if (!group) {
group = { group: g, items: [] };
groups.push(group);
}
group.items.push(d);
})
return groups;
}
And then use it like
{data.groupBy(i => i.group).map((bundle, ix) => (
<div key={ix}>
<b>{bundle.group}</b>
<ul>
{bundle.items.map((item, ix) => <li key={ix}>{item.item}</li>)}
</ul>
</div>
))}
Im very new to javascript, but like hgb123's answer, something like this inside a render or return block:
<div className="myClass">
{myList.map((eachItemInList) => (
<ReactComponent certainProp={eachItemInList} />
))}
</div>
works perfectly for a list like this one:
const myList = ['item1', 'item2', 'item3', 'item4', 'item5', 'item6']
hope this helped someone!
Related
I have an array which contains multiple objects (which have their own array of objects inside them). What I'm trying to do is map through each object's "subMenuItems" array and output the values inside.
Here's my Array:
export const MenuItems = [
{
category: 'Buy',
subMenuItems: [
{
title: `Watching`,
link: '/watching',
},
{
title: `Bids & Offers`,
link: '/bids-offers',
},
{
title: `Bought`,
link: '/bought',
},
],
},
{
category: 'Sell',
subMenuItems: [
{
title: `Selling`,
link: '/selling',
},
{
title: `Sold`,
link: '/sold',
},
{
title: `Sell with us`,
link: '/sell-with-us',
},
],
},
];
I'm able to get each object's category no problem by doing this:
const listItems = MenuItems.map((item, i) => (
<li key={i}>{item.category}</li>
));
Render:
return(
<ul>{listItems}</ul>
)
However I can't seem to get the syntax right when trying to access the "subMenuItems" in each object.
My end goal is to have a function that returns each category with it's sub menu items below it:
E.G.
Buy
- Watching
- Bids & offers
- Bought
Sell
- Selling
- Sold
- Sell With Us
Hope that makes sense :) Thanks!
You can do the same thing and render them using .map:
const listItems = MenuItems.map((item, i) => (
<li key={i}>
{item.category}
<ul>
{item.subMenuItems?.map((index, subMenu) => (
<li key={subMenu.title}> // index as key can be problematic
<a href={subMenu.link}>{subMenu.title}</a>
</li>
)}
</ul>
</li>
));
You could map the subMenuItems into a ul and add it to your outer li
const listItems = MenuItems.map((item, i) => (
const itemlist = item.subMenuItems.map((a, b) => (
<li key={a.title}><a href={a.link}>{a.title}</a></li>
));
<li key={i}>{item.category}<ul>{itemlist}</ul></li>
));
I have an array of objects containing some information. I am not able to render them in the order I want and I need some help with that. I render them like this:
this.state.data.map(
(item, i) => <div key={i}> {item.matchID} {item.timeM} {item.description}</div>
)
Is it possible to sort them ascending with item.timeM in that map()-function or do I have to sort them before i use map?
This might be what you're looking for:
// ... rest of code
// copy your state.data to a new array and sort it by itemM in ascending order
// and then map
const myData = [].concat(this.state.data)
.sort((a, b) => a.itemM > b.itemM ? 1 : -1)
.map((item, i) =>
<div key={i}> {item.matchID} {item.timeM}{item.description}</div>
);
// render your data here...
The method sort will mutate the original array . Hence I create a new array using the concat method. The sorting on the field itemM should work on sortable entities like string and numbers.
You will need to sort your object before mapping over them. And it can be done easily with a sort() function with a custom comparator definition like
var obj = [...this.state.data];
obj.sort((a,b) => a.timeM - b.timeM);
obj.map((item, i) => (<div key={i}> {item.matchID}
{item.timeM} {item.description}</div>))
const list = [
{ qty: 10, size: 'XXL' },
{ qty: 2, size: 'XL' },
{ qty: 8, size: 'M' }
]
list.sort((a, b) => (a.qty > b.qty) ? 1 : -1)
console.log(list)
Out Put :
[
{
"qty": 2,
"size": "XL"
},
{
"qty": 8,
"size": "M"
},
{
"qty": 10,
"size": "XXL"
}
]
this.state.data.sort((a, b) => a.item.timeM > b.item.timeM).map(
(item, i) => <div key={i}> {item.matchID} {item.timeM} {item.description}</div>
)
Try lodash sortBy
import * as _ from "lodash";
_.sortBy(data.applications,"id").map(application => (
console.log("application")
)
)
Read more : lodash.sortBy
this.state.data.sort((a, b) => a.objKey > b.objKey ? 1:-1).map((objKey, index))
Chrome browser considers integer value as return type not boolean value
so,
this.state.data.sort((a, b) => a.item.timeM > b.item.timeM ? 1:-1).map(
(item, i) => <div key={i}> {item.matchID} {item.timeM} {item.description}</div>
)
I found a really good post: React: how to dynamically sort an array of objects
It´s for dropdown, but you can adapt it. The important thing is the way of sorting the array because, as it is explained in the post, react is not realizing about list changes, as the elements are the same.
const [currentList, setCurrentList] = useState(new Array());
const sorted = [...dataList].sort((a, b) => b["lotNumber"] - a["lotNumber"]).reverse();
setCurrentList(sorted);
Use localeCompare for strings.
For many alphabets, it's better to use the string.localeCompare method to properly sort letters like Ö.
For example, let's sort several countries in German:
let countries = ['Österreich', 'Andorra', 'Vietnam']
alert( countries.sort( (a, b) => a > b ? 1 : -1) )
In this case result of sorted array will be next:
Andorra, Vietnam, Österreich (wrong)
alert( countries.sort( (a, b) => a.localeCompare(b) ) )
On the other hand: Andorra, Österreich, Vietnam (properly)
This approach worked for me
const list = [
{ price: 10, plan: 'a' },
{ price: 2, plan: 'b' },
{ price: 8, plan: 'c' }
];
this.setState({ planList: list.sort((a,b)=> a.price-b.price) });
render(){
return(){
<div>
this.state.planList !== undefined && this.state.planList !== null &&
this.state.planList !== '' && this.state.planList.map((ele, index) => {
return (
<div key={index}> {ele.price}{ele.plan}</div>
)
})
</div>
}
}
Thank You
I have a string array with many (100+) words, I have sorted the array in alphabetical order, but I want group or split the array into hash array hashMap<String(Alphabet), List<String>>, so I can show it in this style:
const stringArray = [
"Apple",
"animal",
"Apart",
"ball",
"backpack",
"ballers",
"bingo",
"cars",
"Careful",
"Coach"
]
{stringArray && stringArray?.sort(function (a, b) {
if (a < b) { return -1; }
if (a > b) { return 1; }
return 0;
}).map(word => (
<hr><hr />
<p>{word}</p>
))}
You can easily achieve the result using Map or using plain object as:
Live Demo using Map
Live Demo using object
const arr = [
"Apple",
"animal",
"Apart",
"ball",
"backpack",
"ballers",
"bingo",
"cars",
"Careful",
"Coach",
];
const getFilteredArr = (arr) => {
const map = new Map();
arr
.sort((a, b) => a.localeCompare(b))
.forEach((str) =>
!map.has(str[0].toLowerCase())
? map.set(str[0].toLowerCase(), [str])
: map.get(str[0].toLowerCase()).push(str)
);
return [...map];
};
const newArray = getFilteredArr(arr);
return (
<>
<div>
{newArray.map(([key, values]) => {
return (
<div key={key}>
<h1>{key}</h1>
{values.map((str) => (
<p key={str}>{str}</p>
))}
</div>
);
})}
</div>
</>
);
You don't need a custom sort function, JS sorts alphabetically by default. So first sort your words:
words.sort();
Then iterate over them and add them to a 2nd map that is keyed off of the starting letter:
const letterMap = new Map();
words.forEach(word => {
const letter = word[0].toUpperCase();
if (!letterMap.has(letter)) letterMap.set(letter, []);
letterMap.get(letter).push(word);
});
Now letterMap looks like: {'A': ['Apple', 'Animal', ...], 'B': [...]}, and you can iterate over this using letterMap.entries:
const HTML = [...letterMap.entries()].forEach((letter, words) => {
return `${letter}<hr><hr />` +
`<p>${words.join(' ')}</p>`;
})
Using React, I have a list component that uses array.map to render a list of items.
The list items are variegated; every other list item has a different background color which depends on if the id field of the data structure that feeds the list item is even or odd:
...
const useStyles = makeStyles((theme) => ({
even: {
backgroundColor: theme.palette.background.paper,
},
odd: {
backgroundColor: "#c8c9c7",
},
}));
...
const classes = useStyles();
...
{!list || list.length < 1 ? (
<p>You have no assets selected...</p>
) : (
list.map((items) => (
<ListItem
className={items.id % 2 === 0 ? classes.even : classes.odd}
key={items.id}
>
...
/>
</ListItem>
))
)}
Here is an example of the data structure it uses:
{
{
"id":0,
"foo":"This is a bar"
},
{
"id":1,
"foo":"This is also a bar"
},
{
"id":2,
"foo":"Yes, this too, is a bar"
}
}
I need to remove items. Normal javascript.filter produces non contiguous ids as expected:
{
{
"id":0,
"foo":"This is a bar"
},
{
"id":2,
"foo":"Yes, this too, is a bar"
}
}
I need them to be contiguous:
{
{
"id":0,
"foo":"This is a bar"
},
{
"id":1,
"foo":"Yes, this too, is a bar"
}
}
I have a function that does what I need that needs some tweaking:
const handleRemoveAsset = (id) => {
const arrayCopy = [...assetListItems];
const filteredArray = arrayCopy
.filter((item) => item.id !== id)
for (var i=0; i < filteredArray.length; i++) {
filteredArray[i].id = i;
}
setAssetListItems(filteredArray);
};
This works, but one does not simply for loop using React... I am hoping to use filter and/or map for the entirety of this and not use the for loop that I have.
I read that you can chain filter and map and tried it but couldn't quite work it out. I came up with this:
const filteredArray = array
.filter((item) => item.id !== id)
.map((item, index) => {
item && item.id ? item.id : index)});
... which fails to compile with - expected an assignment to a function call and instead saw an expression on the line after .map.
Any advice at this point would appreciated, thank you!
You could chain map and filter and return the new object from map which updates the pre-existing id.
[...assetListItems]
.filter(item => item.id !== id)
.map((item, index) => ({
...item,
id: index,
}));
I just considered another scenario where if the id is not starting with 0. And if you want the starting id in the resultant array to be as the id of the first object then this is just another way of achieving the expected output.
let data = [{id:0, foo:'This is a bar'},{id:1, foo:'This is also a bar'},{id:2, foo:'Yes, this too, is a bar'}];
const filterItems = (items, id) => {
let lastPushedId = items[0]?.id;
return items.filter(item => item.id !== id).map(item => ({
...item,
id: lastPushedId++
}))
}
console.log(filterItems(data, 1));
//`id` of the first object is `3`
data = [{id:3, foo:'This is a bar'},{id:4, foo:'This is also a bar'},{id:5, foo:'Yes, this too, is a bar'}];
console.log(filterItems(data, 3));
.as-console-wrapper {
max-height: 100% !important;
}
I have an array of objects containing some information. I am not able to render them in the order I want and I need some help with that. I render them like this:
this.state.data.map(
(item, i) => <div key={i}> {item.matchID} {item.timeM} {item.description}</div>
)
Is it possible to sort them ascending with item.timeM in that map()-function or do I have to sort them before i use map?
This might be what you're looking for:
// ... rest of code
// copy your state.data to a new array and sort it by itemM in ascending order
// and then map
const myData = [].concat(this.state.data)
.sort((a, b) => a.itemM > b.itemM ? 1 : -1)
.map((item, i) =>
<div key={i}> {item.matchID} {item.timeM}{item.description}</div>
);
// render your data here...
The method sort will mutate the original array . Hence I create a new array using the concat method. The sorting on the field itemM should work on sortable entities like string and numbers.
You will need to sort your object before mapping over them. And it can be done easily with a sort() function with a custom comparator definition like
var obj = [...this.state.data];
obj.sort((a,b) => a.timeM - b.timeM);
obj.map((item, i) => (<div key={i}> {item.matchID}
{item.timeM} {item.description}</div>))
const list = [
{ qty: 10, size: 'XXL' },
{ qty: 2, size: 'XL' },
{ qty: 8, size: 'M' }
]
list.sort((a, b) => (a.qty > b.qty) ? 1 : -1)
console.log(list)
Out Put :
[
{
"qty": 2,
"size": "XL"
},
{
"qty": 8,
"size": "M"
},
{
"qty": 10,
"size": "XXL"
}
]
this.state.data.sort((a, b) => a.item.timeM > b.item.timeM).map(
(item, i) => <div key={i}> {item.matchID} {item.timeM} {item.description}</div>
)
Try lodash sortBy
import * as _ from "lodash";
_.sortBy(data.applications,"id").map(application => (
console.log("application")
)
)
Read more : lodash.sortBy
this.state.data.sort((a, b) => a.objKey > b.objKey ? 1:-1).map((objKey, index))
Chrome browser considers integer value as return type not boolean value
so,
this.state.data.sort((a, b) => a.item.timeM > b.item.timeM ? 1:-1).map(
(item, i) => <div key={i}> {item.matchID} {item.timeM} {item.description}</div>
)
I found a really good post: React: how to dynamically sort an array of objects
It´s for dropdown, but you can adapt it. The important thing is the way of sorting the array because, as it is explained in the post, react is not realizing about list changes, as the elements are the same.
const [currentList, setCurrentList] = useState(new Array());
const sorted = [...dataList].sort((a, b) => b["lotNumber"] - a["lotNumber"]).reverse();
setCurrentList(sorted);
Use localeCompare for strings.
For many alphabets, it's better to use the string.localeCompare method to properly sort letters like Ö.
For example, let's sort several countries in German:
let countries = ['Österreich', 'Andorra', 'Vietnam']
alert( countries.sort( (a, b) => a > b ? 1 : -1) )
In this case result of sorted array will be next:
Andorra, Vietnam, Österreich (wrong)
alert( countries.sort( (a, b) => a.localeCompare(b) ) )
On the other hand: Andorra, Österreich, Vietnam (properly)
This approach worked for me
const list = [
{ price: 10, plan: 'a' },
{ price: 2, plan: 'b' },
{ price: 8, plan: 'c' }
];
this.setState({ planList: list.sort((a,b)=> a.price-b.price) });
render(){
return(){
<div>
this.state.planList !== undefined && this.state.planList !== null &&
this.state.planList !== '' && this.state.planList.map((ele, index) => {
return (
<div key={index}> {ele.price}{ele.plan}</div>
)
})
</div>
}
}
Thank You