Reputation: 393
I've written the following IComparer but I need some help. I'm trying to sort a list of numbers but some of the numbers may not have been filled in. I want these numbers to be sent to the end of the list at all times.. for example...
[EMPTY], 1, [EMPTY], 3, 2
would become...
1, 2, 3, [EMPTY], [EMPTY]
and reversed this would become...
3, 2, 1, [EMPTY], [EMPTY]
Any ideas?
public int Compare(ListViewItem x, ListViewItem y)
{
int comparison = int.MinValue;
ListViewItem.ListViewSubItem itemOne = x.SubItems[subItemIndex];
ListViewItem.ListViewSubItem itemTwo = y.SubItems[subItemIndex];
if (!string.IsNullOrEmpty(itemOne.Text) && !string.IsNullOrEmpty(itemTwo.Text))
{
uint itemOneComparison = uint.Parse(itemOne.Text);
uint itemTwoComparison = uint.Parse(itemTwo.Text);
comparison = itemOneComparison.CompareTo(itemTwoComparison);
}
else
{
// ALWAYS SEND TO BOTTOM/END OF LIST.
}
// Calculate correct return value based on object comparison.
if (OrderOfSort == SortOrder.Descending)
{
// Descending sort is selected, return negative result of compare operation.
comparison = (-comparison);
}
else if (OrderOfSort == SortOrder.None)
{
// Return '0' to indicate they are equal.
comparison = 0;
}
return comparison;
}
Cheers.
Upvotes: 3
Views: 3545
Reputation: 51711
I'd actually approach this a completely different way, remove the empty slots, sort the list, then add the empty ones to the end of the list
static void Main(string[] args)
{
List<string> ints = new List<string> { "3", "1", "", "5", "", "2" };
CustomIntSort(ints, (x, y) => int.Parse(x) - int.Parse(y)); // Ascending
ints.ForEach(i => Console.WriteLine("[{0}]", i));
CustomIntSort(ints, (x, y) => int.Parse(y) - int.Parse(x)); // Descending
ints.ForEach(i => Console.WriteLine("[{0}]", i));
}
private static void CustomIntSort(List<string> ints, Comparison<string> Comparer)
{
int emptySlots = CountAndRemove(ints);
ints.Sort(Comparer);
for (int i = 0; i < emptySlots; i++)
ints.Add("");
}
private static int CountAndRemove(List<string> ints)
{
int emptySlots = 0;
int i = 0;
while (i < ints.Count)
{
if (string.IsNullOrEmpty(ints[i]))
{
emptySlots++;
ints.RemoveAt(i);
}
else
i++;
}
return emptySlots;
}
This question caught my attention recently, this comparer will do it either
class CustomComparer
: IComparer<string>
{
private bool isAscending;
public CustomComparer(bool isAscending = true)
{
this.isAscending = isAscending;
}
public int Compare(string x, string y)
{
long ix = CustomParser(x) * (isAscending ? 1 : -1);
long iy = CustomParser(y) * (isAscending ? 1 : -1);
return ix.CompareTo(iy) ;
}
private long CustomParser(string s)
{
if (string.IsNullOrEmpty(s))
return isAscending ? int.MaxValue : int.MinValue;
else
return int.Parse(s);
}
}
Upvotes: 2
Reputation: 12954
else
{
//ALWAYS SEND TO BOTTOM/END OF LIST.
if (string.IsNullOrEmpty(itemOne.Text) && string.IsNullOrEmpty(itemTwo.Text))
{
return 0;
}
else if (string.IsNullOrEmpty(itemOne.Text))
{
return -1;
}
else if (string.IsNullOrEmpty(itemTwo.Text))
{
return 1;
}
}
Upvotes: 0
Reputation: 499182
Always return 1 for your empty
x values and -1 for your empty
y values. This will mean that the comparer sees empty values as the greater value in all cases so they should end up at the end of the sorted list.
Of course, if both are empty, you should return 0 as they are equal.
Upvotes: 0
Reputation: 72668
Your logic is slightly off: your else
will be entered if either of them are empty, but you only want the empty one to go to the end of the list, not the non-empty one. Something like this should work:
public int Compare(ListViewItem x, ListViewItem y)
{
ListViewItem.ListViewSubItem itemOne = x.SubItems[subItemIndex];
ListViewItem.ListViewSubItem itemTwo = y.SubItems[subItemIndex];
// if they're both empty, return 0
if (string.IsNullOrEmpty(itemOne.Text) && string.IsNullOrEmpty(itemTwo.Text))
return 0;
// if itemOne is empty, it comes second
if (string.IsNullOrEmpty(itemOne.Text))
return 1;
// if itemTwo is empty, it comes second
if (string.IsNullOrEmpty(itemTwo.Text)
return -1;
uint itemOneComparison = uint.Parse(itemOne.Text);
uint itemTwoComparison = uint.Parse(itemTwo.Text);
// Calculate correct return value based on object comparison.
int comparison = itemOneComparison.CompareTo(itemTwoComparison);
if (OrderOfSort == SortOrder.Descending)
comparison = (-comparison);
return comparison;
}
(I might've got the "1" and "-1" for when they're empty back to front, I can never remember :)
Upvotes: 4
Reputation: 457
Your // ALWAYS SEND TO BOTTOM/END OF LIST. branch is being executed when either the x or y parameters are empty, i.e. a non-empty value will be sorted according to this rule if it is being compared to an empty value. You probably want something more like this:
if (!string.IsNullOrEmpty(itemOne.Text) && !string.IsNullOrEmpty(itemTwo.Text))
{
uint itemOneComparison = uint.Parse(itemOne.Text);
uint itemTwoComparison = uint.Parse(itemTwo.Text);
comparison = itemOneComparison.CompareTo(itemTwoComparison);
}
else if (!string.IsNullOrEmpty(itemOne.Text)
{
comparison = -1;
}
else
{
comparison = 1;
}
Upvotes: 0