I want to write unique initials before listing an array of names, so given const names = ["Bill", "Jack", "john"], I would like to print something like:
<ul>
<li>B
<ul>
<li>Bill</li>
</ul>
</li>
<li>J
<ul>
<li>John</li>
<li>jack</li>
</ul>
</li>
</ul>
The way I found to do this is to push the JSX into an array before rendering it like:
const RenderNames = () => {
let initials = [];
let renderData = [];
names.forEach(name => {
let initial = name.charAt(0).toUpperCase();
if(initials.indexOf(initial) === -1){
initials.push(initial)
renderData.push(<li>{initial}</li>)
}
renderData.push(<li>{name}</li>)
});
return <ul>{renderData}</ul>;
}
But I feel the code is a bit clunky, and I can only push in tags that are immediately closing. Is this the best way to do things or could it be done better?
CodePudding user response:
Here we go:
const names = ['Bill', 'Jack', 'john', 'Alex'];
const groupedNames = names.reduce((accumulator, name) => {
// first char of name, uppercased
const firstLetter = name[0].toUpperCase();
// check if data for key exist
const namesList = accumulator[firstLetter] || [];
// check if name in array exist to prevent duplicates
// keep in mind for example John and john are not the same
if (!namesList.includes(name)) {
namesList.push(name);
}
// collect data and return
return {...accumulator, [firstLetter]: namesList}
}, {});
and result is
{ B: [ 'Bill' ], J: [ 'Jack', 'john' ], A: [ 'Alex' ] }
Then you can sort keys and map()
over it.
CodePudding user response:
You can use this::
const names = ['Bill', 'John', 'bob', 'Jack'];
const category = names.reduce((unique, name) => {
const char = name.charAt(0).toUpperCase();
return (unique[char] ? { ...unique, [char]: [...unique[char], name] } : { ...unique, [char]: [name] });
}, {});
return (
<div className="App">
<ul>
{
Object.entries(category).map(([key, value]) =>
<ul>
<li>{key}</li>
{
value.length &&
<ul>
{
value.map(name =>
<li>{name}</li>
)
}
</ul>
}
</ul>
)}
</ul>
</div>
);
Check this link:
https://codesandbox.io/s/vigilant-sound-ukv0q?file=/src/App.js