I want to sort entries in a given list on Entry column in most efficient way. Here is how my entries look:
#No Detail Entry Number Rate
1 Carpool at 5$ C 1 5
Carpool at 5$ H 2 5
2 Played Cricket at 2$ X 1 2
Played Cricket at 2$ O 2 2
3 Done something at 4$ "" 0 4
4 Done something else at 9$ M 1 9
5 Watched movie at 6$ B 1 6
Watched movie at 6$ Z 2 6
Some explanation of the format:
- #No column is not present in table. I just mentioned here to give detail about entries.
- #1,2,5 are special "clubbed" entries and should be sorted together on Entry which have number column set to 1. These entries have specialty that their rate and description would be same and Number would be 1 and 2.
- There is possibility that there could be some entries which have empty Entry values; such entries will have number set to 0. Such as #3
- There is possibility that there could be some entries which have single Entry value and will not have any pair and Number column for such entries would be set to 1 and there wouldn't be any entry in list which have same description and rate with Number column set to 2. Such as #4.
- While sorting clubbed entries on Entry column only consider Entry with Number set to 1 and other entry should tag along.
I want to sort Entry column by ascending or descending order abiding above rules.
My Solution:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
internal interface IDBEntity
{
string Detail { get; set; }
string Entry { get; set; }
int Number { get; set; }
int Rate { get; set; }
}
internal class Data : IDBEntity
{
public string Detail { get; set; }
public string Entry { get; set; }
public int Number { get; set; }
public int Rate { get; set; }
}
internal enum SortingOrder
{
Ascending,
Descending
}
internal enum SortingType
{
Detail,
Entry,
Number,
Rate
}
internal class Comparer<T> : IComparer<T> where T : IDBEntity
{
public SortingType SortingColumn { get; set; }
private int sortOrder = 1;
public Comparer(SortingOrder order = SortingOrder.Ascending)
{
if (order == SortingOrder.Ascending)
{
sortOrder = 1;
}
else if (order == SortingOrder.Descending)
{
sortOrder = -1;
}
else
{
throw new NotImplementedException("Sorting Order is Undefined");
}
}
public int Compare(T x, T y)
{
int result = sortOrder;
if (x == null && y != null)
{
result *= sortOrder;
}
else if (x == null && y == null)
{
result = 0;
}
switch (SortingColumn)
{
case SortingType.Entry:
if (x.Detail == y.Detail && x.Rate == y.Rate)
{
result = x.Entry.CompareTo(y.Entry);
}
break;
default:
break;
}
return result * sortOrder;
}
}
internal class Program
{
private static void Main(string[] args)
{
List<Data> entries = new List<Data>();
// Clubbed entry...While sorting only consider entry with Number set to 1. They will have same rate and Detail.
entries.Add(new Data() { Detail = "Carpool at 5$", Entry = "C", Number = 1, Rate = 5 });
entries.Add(new Data() { Detail = "Carpool at 5$", Entry = "H", Number = 2, Rate = 5 });
// Clubbed entry
entries.Add(new Data() { Detail = "Played Cricket at 2$", Entry = "X", Number = 1, Rate = 2 });
entries.Add(new Data() { Detail = "Played Cricket at 2$", Entry = "O", Number = 2, Rate = 2 });
// entry which have empty Entry value such entries will have Number set to 0
entries.Add(new Data() { Detail = "Done something at 4$", Entry = "", Number = 0, Rate = 4 });
// entry which will not have an pair and Number column for such entries would be set to 1 and
// there wouldn't be any entry in list which have same detail and rate with Number coloumn set to 2
entries.Add(new Data() { Detail = "Done something else at 9$", Entry = "M", Number = 1, Rate = 9 });
// Clubbed entry
entries.Add(new Data() { Detail = "Watched movie at 6$", Entry = "B", Number = 1, Rate = 6 });
entries.Add(new Data() { Detail = "Watched movie at 6$", Entry = "Z", Number = 2, Rate = 6 });
entries.Add(new Data() { Detail = "BlahBlah", Entry = "", Number = 0, Rate = 0 });
// Sorting on Entry Coloumn
var sortedList = entries.GroupBy(x => new { x.Detail, x.Rate }) // Grouping entries which have same Detail and Rate
.OrderBy(x => x.FirstOrDefault(y => y.Number <= 1)?.Entry) // Sort the group based on Entry which has Number set to <=1
.SelectMany(x => x.OrderBy(z => z.Number)) // Un-Group entries and make sure order is maintained using Number property
.ToList();
var compObj = new Comparer<Data>() { SortingColumn = SortingType.Entry };
entries.Sort(compObj);
}
}
}
LINQ solution
var sortedList = entries.GroupBy(x => new { x.Detail, x.Rate }) // Grouping entries which have same Detail and Rate
.OrderBy(x => x.FirstOrDefault(y => y.Number <= 1)?.Entry) // Sort the group based on Entry which has Number set to <=1
.SelectMany(x => x.OrderBy(z => z.Number)) // Un-Group entries and make sure order is maintained using Number property
.ToList();
Output:
Detail Entry Number Rate
Done something at 4$ "" 0 4
Watched movie at 6$ B 1 6
Watched movie at 6$ Z 2 6
Carpool at 5$ C 1 5
Carpool at 5$ H 2 5
Done something else at 9$ M 1 9
Played Cricket at 2$ X 1 2
Played Cricket at 2$ O 2 2
Questions:
- I have Linq solution working, Any comments on that?
- Can it be done using
IComparer
as in rest of my code had sorting done usingIComparer
? InCompare
method, I have switch case on column where I compare two entries but I don't know how to group while comparing.
OrderBy
in your LINQ statement. The comparer isn't even aware of collections that single instances happen to be in. \$\endgroup\$