Skip to content

Instantly share code, notes, and snippets.

@nickalbrecht
Last active November 1, 2023 23:19
Show Gist options
  • Save nickalbrecht/549917881c2f78547366001a77a0b391 to your computer and use it in GitHub Desktop.
Save nickalbrecht/549917881c2f78547366001a77a0b391 to your computer and use it in GitHub Desktop.
Custom Comparer<SelectListItem> implementation for use in ASPNETCore to handle prioritizing certain SelectListGroup to the top of the SELECT element before being passed to a View. NULL groups are put last, but the SelectListItem, with a NULL value is made first as I assume it's the "Pick One" place holder
public class PrioritizedGroupsSelectListItemComparer : Comparer<SelectListItem>
{
private readonly List<string> prioritizedGroupNames;
public PrioritizedGroupsSelectListItemComparer(params string[] prioritizedGroupNames)
{
this.prioritizedGroupNames = prioritizedGroupNames.ToList();
}
public PrioritizedGroupsSelectListItemComparer(IEnumerable<string> prioritizedGroupNames)
{
this.prioritizedGroupNames = prioritizedGroupNames.ToList();
}
public override int Compare(SelectListItem item1, SelectListItem item2)
{
var item1IsEmpty = string.IsNullOrEmpty(item1.Value);
var item2IsEmpty = string.IsNullOrEmpty(item2.Value);
if (item1IsEmpty && !item2IsEmpty) return -1; // Only item1 has empty value, it should come first
if (!item1IsEmpty && item2IsEmpty) return 1; // Only item2 has empty value, it should come first
// technically they should never both be empty, as it's asummed only one item in the list will have an empty value, since it should be the "Pick one" option
var index1 = prioritizedGroupNames.IndexOf(item1.Group?.Name);
var index2 = prioritizedGroupNames.IndexOf(item2.Group?.Name);
// *** Both are in priority groups
if (index1 != -1 && index2 != -1) {
var comparison = index1.CompareTo(index2); //Compare based on the order the groups appear in the priority groups
if (comparison == 0) // If both in the same group...
{
return string.Compare(item1.Text, item2.Text, StringComparison.CurrentCultureIgnoreCase); //..Then compare items alphabetically
}
return comparison; // Otherwise use the result of comparing the order in the priority group
}
// *** Only one is in a priority group
if (index1 != -1) return -1;
if (index2 != -1) return 1;
// *** Neither are in priority groups
// Both groups are empty
if (item1.Group == null && item2.Group == null) {
return string.Compare(item1.Text, item2.Text, StringComparison.CurrentCultureIgnoreCase);
}
// At least one group is empty, bump empty groups down
// Otherwise they'd be at the top, just below prioritized groups
if (item1.Group == null) return 1;
if (item2.Group == null) return -1;
// Both are non-null, non-priority groups, order groups alphabetically
var unprioritizedGroupComparison = string.Compare(item1.Group.Name, item2.Group.Name, StringComparison.CurrentCultureIgnoreCase);
if(unprioritizedGroupComparison == 0)
return string.Compare(item1.Text, item2.Text, StringComparison.CurrentCultureIgnoreCase);
else
return unprioritizedGroupComparison;
}
}
public static class SuggestedSelectListItemExtensionMethods
{
private const string suggested = "Suggested";
/// <summary>
/// Performs an <see cref="Enumerable.OrderBy()"/> on <see cref="SelectListItem"/>s, using a custom <see cref="IComparer&lt;&gt;"/> to order by <see cref="SelectListGroup.Name"/>, then by <see cref="SelectListItem.Text"/>. "Suggested" group is bumped to the top
/// </summary>
public static IOrderedEnumerable<SelectListItem> OrderBySuggested<TEnumerableSelectListItems>(this TEnumerableSelectListItems items) where TEnumerableSelectListItems : IEnumerable<SelectListItem>
{
return items.OrderByPrioritizedGroups(suggested);
}
/// <summary>
/// Performs an <see cref="Enumerable.OrderBy()"/> on <see cref="SelectListItem"/>s, using a custom <see cref="IComparer&lt;&gt;"/> to order by <see cref="SelectListGroup.Name"/>, then by <see cref="SelectListItem.Text"/>. Prioritized group names are bumped to the top
/// </summary>
public static IOrderedEnumerable<SelectListItem> OrderByPrioritizedGroups<TEnumerableSelectListItems>(this TEnumerableSelectListItems items, IEnumerable<string> prioritizedGroupNames) where TEnumerableSelectListItems : IEnumerable<SelectListItem>
{
return items.OrderBy(x => x, new PrioritizedGroupsSelectListItemComparer(prioritizedGroupNames));
}
/// <summary>
/// Performs an <see cref="Enumerable.OrderBy()"/> on <see cref="SelectListItem"/>s, using a custom <see cref="IComparer&lt;&gt;"/> to order by <see cref="SelectListGroup.Name"/>, then by <see cref="SelectListItem.Text"/>. Prioritized group names are bumped to the top
/// </summary>
public static IOrderedEnumerable<SelectListItem> OrderByPrioritizedGroups<TEnumerableSelectListItems>(this TEnumerableSelectListItems items, params string[] prioritizedGroupNames) where TEnumerableSelectListItems : IEnumerable<SelectListItem>
{
return items.OrderBy(x => x, new PrioritizedGroupsSelectListItemComparer(prioritizedGroupNames));
}
/// <summary>
/// Performs a <see cref="List{SelectListItem}.Sort()"/> on <see cref="SelectListItem"/>s, using a custom <see cref="IComparer&lt;&gt;"/> to order by <see cref="SelectListGroup.Name"/>, then by <see cref="SelectListItem.Text"/>. "Suggested" group is bumped to the top
/// </summary>
public static void SortBySuggested(this List<SelectListItem> items)
{
items.SortByPrioritizedGroups(suggested);
}
/// <summary>
/// Performs a <see cref="List{SelectListItem}.Sort()"/> on <see cref="SelectListItem"/>s, using a custom <see cref="IComparer&lt;&gt;"/> to order by <see cref="SelectListGroup.Name"/>, then by <see cref="SelectListItem.Text"/>. Prioritized group names are bumped to the top
/// </summary>
public static void SortByPrioritizedGroups(this List<SelectListItem> items, IEnumerable<string> prioritizedGroupNames)
{
items.Sort(new PrioritizedGroupsSelectListItemComparer(prioritizedGroupNames));
}
/// <summary>
/// Performs a <see cref="List{SelectListItem}.Sort()"/> on <see cref="SelectListItem"/>s, using a custom <see cref="IComparer&lt;&gt;"/> to order by <see cref="SelectListGroup.Name"/>, then by <see cref="SelectListItem.Text"/>. Prioritized group names are bumped to the top
/// </summary>
public static void SortByPrioritizedGroups(this List<SelectListItem> items, params string[] prioritizedGroupNames)
{
items.Sort(new PrioritizedGroupsSelectListItemComparer(prioritizedGroupNames));
}
}
@nickalbrecht
Copy link
Author

Updated to have overloads accepting params string instead of having to always create enumerable parameters

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment