Custom Sort Logic in Orderby Using Linq

Custom sort logic in OrderBy using LINQ

If you want custom ordering, but don't want to supply a comparer, you can have it - sql style:

autoList
.OrderBy(a => a.StartsWith("_") ? 2 : 1 )
.ThenBy(a => a);

Linq Custom Sorting

Just reverse your first two conditions:

ListOfObjects.OrderBy(t => t.letter.StartsWith("c") ? 2 : 1)
.ThenBy(t => t.letter)
.ThenBy(t => t.id);

The first condition moves all cs to the end, then sorts by letter, then by id.

Since your first condition was already sorting by letter, trying to sort again based on letter had no effect.

Sorting list of strings using linq with custom sort

This code worked for me.

 var revList = new List<string>  { "A", "NC", "New", "AB", "PD", "PD1",
"PD2", "B", "-", "*", "BB", "NA" };

revList = revList.OrderByDescending(i => i.ToLower() == "pd").
ThenByDescending(i => i.ToLower() == "nc").
ThenByDescending(i => i.ToLower() == "na").
ThenByDescending(i => i.ToLower() == "new").
ThenByDescending(i => i.ToLower() == "pd1").
ThenByDescending(i => i.ToLower() == "pd2").
ThenByDescending(i => i.ToLower() == "-").
ThenByDescending(i => i.ToLower() == "*").
ThenBy(i => i.Length).ToList();

foreach (string rev in revList)
Console.WriteLine(rev);
Console.ReadLine();

LINQ Custom Sort

You need to use a comparison function, they are functions that from two instances of your type return an integer that return 0 if both are equals, a negative value if the first is less than the second and a positive value if the first is greater than the second.

MSDN has a nice table that is easier to follow than text (StackOverflow still doesn't support tables in 2014)

IComparer<T>

Most sort methods accept a custom comparer implementation of type IComparer<T> you should create one encapsulating your custom rules for Group :

class GroupComparer : IComparer<Group>
{
public int Compare(Group a, Group b)
{
if (a != null && b != null && (a.Id == 0 || b.Id == 0))
{
if (a.Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return a.Id == 0 ? -1 : 1;
}

if (a == null || b == null)
{
if (ReferenceEquals(a, b))
{
return 0;
}
return a == null ? -1 : 1;
}
return Comparer<string>.Default.Compare(a.Name, b.Name);
}
}

Usage:

items.OrderBy(_ => _, new GroupAuthorityComparer());



IComparable<T>

If it is the only way to compare Group instances you should make it implement IComparable<T> so that no aditional code is needed if anyone want to sort your class :

class Group : IComparable<Group>
{
...

public int CompareTo(Group b)
{
if (b != null && (Id == 0 || b.Id == 0))
{
if (Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return Id == 0 ? -1 : 1;
}

return Comparer<string>.Default.Compare(Name, b.Name);
}
}

Usage:

items.OrderBy(_ => _.Group);

The choice between one way or the other should be done depending on where this specific comparer is used: Is it the main ordering for this type of item or just the ordering that should be used in one specific case, for example only in some administrative view.

You can even go one level up and provide an IComparable<GroupAuthority> implementation (It's easy once Group implement IComparable<Group>):

class GroupAuthority : IComparable<GroupAuthority>
{
...

public int CompareTo(GroupAuthority b)
{
return Comparer<Group>.Default.Compare(Group, b.Group);
}
}

Usage:

items.OrderBy(_ => _);

The advantage of the last one is that it will be used automatically, so code like: GroupAuthoritys.ToList().Sort() will do the correct thing out of the box.

LINQ OrderBy custom order

Maybe you want to do something like this:

char [] customOrder = { 'D', 'G', 'R', 'C'};
char [] c = new char[] { 'G', 'R', 'D', 'D', 'G', 'R',
'R', 'C', 'D', 'G', 'R', 'R',
'C', 'G', 'R', 'D', 'D', 'G',
'R', 'R', 'C', 'D', 'G', 'R', 'R', 'C' };

foreach (char item in c.OrderBy(ch => Array.IndexOf(customOrder, ch))) {
Console.Write(item);
}

Custom Sort in C# LINQ with LAMBDA

ORDER BY 
CASE
WHEN UserID = 123 THEN 0
WHEN UserID = 456 THEN 1
WHEN UserID = 789 THEN 2
ELSE 3
END ASC,
UserID

is functionally equivalent to this Lambda

.OrderBy(_ => _.userID == 123 ? 0 : _.userID == 456 ? 1 : _.userID == 789 ? 2 : 3)
.ThenBy(_ => _.userID)

Difficulty with LINQ Query writing custom sort logic

This seems to be what what you want:

var shortfilenames = new List<string>(){"Avinash_Create.sql" , "Avinash_Insert.sql" , "Avinash_Update.sql" , "Avinash_Delete.sql"};
var userGroups = shortfilenames
.Select(fn =>
{
string fileName = Path.GetFileNameWithoutExtension(fn);
string[] nameAndAction = fileName.Split('_');
return new
{
extension = Path.GetExtension(fn),
fileName,
name = nameAndAction[0],
action = nameAndAction[1]
};
})
.GroupBy(x => x.name)
.Select(g => g.OrderByDescending(x => x.action.Equals("CREATE", StringComparison.InvariantCultureIgnoreCase))
.ThenByDescending(x => x.action.Equals("INSERT", StringComparison.InvariantCultureIgnoreCase))
.ThenByDescending(x => x.action.Equals("UPDATE", StringComparison.InvariantCultureIgnoreCase))
.ThenByDescending(x => x.action.Equals("DELETE", StringComparison.InvariantCultureIgnoreCase))
.ToList());

foreach (var ug in userGroups)
foreach (var x in ug)
Console.WriteLine("{0} {1}", x.name, x.action);

prints out:

Avinash Create
Avinash Insert
Avinash Update
Avinash Delete

Presumes that the file-names always contain the underscore.

Custom orderby in linq to sql

Here is an approach using a lambda expression


MyTable
.OrderBy (t => (t.Season == "Winter") ? 1 : (t.Season == "Spring") ? 2 : [...])
.Select (
t => new

{
MyColumn = t.MyColumn
...
}
)

Custom Sorting using c# Linq Expressions

First off, I've previously tried to re-invent the wheel like this myself, and it never ever really works as well as you'd like. If you need that sort of dynamic flexiblity, then either there is probably already a library somewhere, or you may as well drop to actually manually crafting SQL or something (it sucks, but sometimes it's the only pragmatic approach).. That aside...

I think your problem is actually related to SQLite - I couldn't get the SQLite stuff to work due to either typos or versions not being the same (e.g. The default nuget packages for SQLite have a SQLiteConnectionStringBuilder and NOT a SqliteConnectionStringBuilder, and this doesn't appear to have the same properties as your example) so I hacked your code somewhat to remove the SQL stuff and get rid of Async things (as I would hope that that's not relevant really), so I have this repository instead:

public class TestDecimalPropertyClassRepository
{
private readonly IList<TestDecimalPropertyClass> list;

public TestDecimalPropertyClassRepository(IEnumerable<TestDecimalPropertyClass> repo)
{
list = repo.ToList();
}

public IEnumerable<TestDecimalPropertyClass> GetAll(Sorting sorting)
{
List<TestDecimalPropertyClass> entities = list
.AsQueryable()
.SortBy(sorting)
.ToList();

return entities;
}

public void Save(TestDecimalPropertyClass testDecimalPropertyClass)
{
list.Add(testDecimalPropertyClass);

}
}

Which makes the test look like this

[Test]
public void GivenADecimalProperty_WhenISortByColumn_ThenItSorts()
{
decimal[] decimals = new[] { 7m, 84.3m, 13.4m };
var repo = decimals.Select(x => new TestDecimalPropertyClass(x));

TestDecimalPropertyClassRepository testRepository = new TestDecimalPropertyClassRepository(repo);

var entities = testRepository.GetAll(new Sorting
{
SortableEntities = new[]
{
new SortableEntity
{
Descending = false,
Name = "decimal",
Order = 0
}
}
});

List<TestDecimalPropertyClass> list = entities.ToList();
Assert.That(list.Count(), Is.EqualTo(decimals.Length));
Assert.That(list.ToArray()[0].Decimal, Is.EqualTo(7m));
Assert.That(list.ToArray()[1].Decimal, Is.EqualTo(13.4m));
Assert.That(list.ToArray()[2].Decimal, Is.EqualTo(84.3m));
}

And left all your extension stuff the same, so it's still reflecting around etc. in the same way.

This test passes fine. Now, this isn't really entirely valid as it's no longer quite the same of course, but it does to my mind mean it's probably not the framework mis-interpreting the type of the decimal property, or some sort of confusion related to boxing/unboxing meaning it can't work out the type and does a .ToString() for comparison.

Assuming the SQLite EF provider is correctly translating this into a SQL ORDER BY clause, have you checked this SQL? In the past I've done similar (used SQLite to write tests) and found it's not quite as complete in some obscure ways as SQL Server or similar. Perhaps the provider has a bug, or there's a quirk in the generated expression tree that it can't quite understand well enough.

So I'd look into that first rather than the code you've written..



Related Topics



Leave a reply



Submit