Reputation: 93
Basically, I have a bunch of categories that pharmaceutical drugs are classed in. I also have individual drugs that have a class.
Right now, my code basically maps all categories from the category array and if the class of the drug matches the category, it places it underneath it.
However, I only want to render a category in the drop-down if drug.class matches it.
tldr: drug.class doesn't match a category? then don't show the category in the dropdown. drug.class matches a category? render the category and place the drug underneath that category, however, do NOT repeat the category for every drug.
Basically this rendering ONLY if drug.class matches the category.name, so I don't have empty categories with no drugs underneath.
<ListSubheader key={category.id}>{category.name}</ListSubheader>
I hope I've explained this to the best of my ability, here's my code:
{categories.map(category =>
(<span>
<ListSubheader key={category.id}>{category.name}</ListSubheader>
{drugs.map(drug => drug.class.toLowerCase()===category.name.toLowerCase() ?
<>
{ patient.age <= 12 && drug.suggestedDosePediatric != "Restricted" &&
<MenuItem key={drug._id} onClick={()=>handleClick(drug)} value={drug.value}>{drug.name}</MenuItem>
}
{ patient.age > 12 &&
<MenuItem key={drug._id} onClick={()=>handleClick(drug)} value={drug.value}>{drug.name}</MenuItem>
}
</>
: null)}
</span>)
)}
Upvotes: 0
Views: 206
Reputation: 21110
You could group the drugs by class.
// assuming the following helper
function groupBy(iterable, keyFn) {
const groups = new Map();
for (const item of iterable) {
const key = keyFn(item);
if (!groups.has(key)) groups.set(key, []);
groups.get(key).push(item);
}
return groups;
}
With the above helper (or something similar from a library) we can group the drugs and then iterate over them.
if (patient.age <= 12) {
drugs = drugs.filter(drug => drug.suggestedDosePediatric != "Restricted");
}
const drugsByCategoryName = groupBy(drugs, drug => drug.class.toLowerCase());
// ...
{Array.from(drugsByCategoryName, ([categoryName, drugs]) => (
<span>
<ListSubheader>{categoryName}</ListSubheader>
{drugs.map((drug) => (
<MenuItem key={drug._id} onClick={()=>handleClick(drug)} value={drug.value}>
{drug.name}
</MenuItem>
))}
</span>
))}
The above essentially answers your question. Although you might have noticed that the category is currently only a name (key={category.id}
is missing). To get access to all properties of a category it's a good idea to create a lookup object for categories first, since you'll have to find the category belonging to each drug.
const categoriesByName = new Map(categories.map((category) => (
[category.name.toLowerCase(), category]
)));
Then instead of grouping by category name, group the drug by the category object.
const drugsByCategory = groupBy(drugs, (drug) => (
categoriesByName.get(drug.class.toLowerCase()))
));
This new definition of drugsByCategory
is grouped by category
object, not just the name. Which allows you access to the category properties when iterating over it.
{Array.from(drugsByCategoryName, ([category, drugs]) => (
<span>
<ListSubheader key={category.id}>{category.name}</ListSubheader>
{/* ... */}
</span>
))}
There is still a small "loophole" within the above code that could result in an exception. What if the list of drugs
includes a drug.class
that is not present within the categories
? Such a scenario might not exist, in which case you don't have to worry about it. If this scenario does exist the above code already handles it pretty well. categoriesByName.get(drug.class.toLowerCase()))
will return undefined
, meaning that all drugs with a non-existent drug.class
are listed under the undefined
key within the drugsByCategory
Map.
This allow for different solutions. You could ignore all drugs with a non-existent drug.class
by removing the group from the collection.
drugsByCategory.delete(undefined);
Another option would be to add an "others" category at the end.
const others = drugsByCategory.get(undefined);
drugsByCategory.delete(undefined);
// ...
{Array.from(drugsByCategory, (category, drugs) => (
<span>
<ListSubheader key={category.id}>{category.name}</ListSubheader>
{/* ... */}
</span>
))}
{others && (
<span>
<ListSubheader key="others">Others</ListSubheader>
{/* ... */}
</span>
)}
Upvotes: 1
Reputation: 2129
drugs.filter(drug => drug.class.toLowerCase() === category.name.toLowerCase()
will give you the array with only the drugs you want. You can then map it. They can be chained together, like this:
{categories.map(category =>
(<span>
<ListSubheader key={category.id}>{category.name}</ListSubheader>
{drugs.filter(drug => drug.class.toLowerCase() === category.name.toLowerCase()).map(drug => drug.class.toLowerCase()===category.name.toLowerCase() ?
<>
{ patient.age <= 12 && drug.suggestedDosePediatric != "Restricted" &&
<MenuItem key={drug._id} onClick={()=>handleClick(drug)} value={drug.value}>{drug.name}</MenuItem>
}
{ patient.age > 12 &&
<MenuItem key={drug._id} onClick={()=>handleClick(drug)} value={drug.value}>{drug.name}</MenuItem>
}
</>
: null)}
</span>)
)}
Upvotes: 0