Natural Sort Order in C#

Natural Sort Order in C#

The easiest thing to do is just P/Invoke the built-in function in Windows, and use it as the comparison function in your IComparer:

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern int StrCmpLogicalW(string psz1, string psz2);

Michael Kaplan has some examples of how this function works here, and the changes that were made for Vista to make it work more intuitively. The plus side of this function is that it will have the same behaviour as the version of Windows it runs on, however this does mean that it differs between versions of Windows so you need to consider whether this is a problem for you.

So a complete implementation would be something like:

[SuppressUnmanagedCodeSecurity]
internal static class SafeNativeMethods
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern int StrCmpLogicalW(string psz1, string psz2);
}

public sealed class NaturalStringComparer : IComparer<string>
{
public int Compare(string a, string b)
{
return SafeNativeMethods.StrCmpLogicalW(a, b);
}
}

public sealed class NaturalFileInfoNameComparer : IComparer<FileInfo>
{
public int Compare(FileInfo a, FileInfo b)
{
return SafeNativeMethods.StrCmpLogicalW(a.Name, b.Name);
}
}

Natural Sorting of list of string in descending order C#

Let's implement a simple extension method:

  public static partial class ComparerExtensions {
public static IComparer<T> Reverse<T>(this IComparer<T> comparer) {
if (null == comparer)
throw new ArgumentNullException(nameof(comparer));

return Comparer<T>.Create((left, right) => comparer.Compare(right, left));
}
}

Then you can reverse any comparer (ICompare<T>) you like:

 MyList.Sort(YourCustomComparer.Reverse());

In your case (a bit strange implmentation with comparer implementing IDisposable):

 using (var naturalComparer = new NaturalComparer()) {
contents.Sort(naturalComparer.Reverse());
}

Edit: In case of C# 4.0 or earlier version (which doesn't have Comparer<T>.Create) we can implement the extension method like this:

  public static partial class ComparerExtensions {
private sealed class ReversedComparer<T> : IComparer<T> {
private readonly IComparer<T> m_Comparer;

public ReversedComparer(IComparer<T> comparer) {
m_Comparer = comparer;
}

public int Compare(T x, T y) {
return m_Comparer.Compare(y, x);
}
}

public static IComparer<T> Reverse<T>(this IComparer<T> comparer) {
if (null == comparer)
throw new ArgumentNullException(nameof(comparer));

return new ReversedComparer<T>(comparer);
}
}

Natural sort order in C# - implementation

I believe you want to use it like this:

DataTable dtNew = dv.Table.AsEnumerable().OrderByAlphaNumeric(x => x.Field<string>("code_name")).CopyToDataTable();

Be sure to open the namespace containing OrderByAlphaNumeric where you use this code.

Natural Sorting in LINQ

Are you certain that every Object.Order is a colon separated string of at least 3 integer values?

Why not convert these three values to integer and sort by the parsed values?

var result = myInputSequence.Select(sourceItem => new
{

SplitOrderNumbers = (sourceItem.Order.Split(';')
.Select(splitItem => Int32.Parse(splitItem))
.Take(3)
.ToList(),
OriginalSourceItem = sourceItem,
})
.OrderBy(item => item.SplitOrderNumbers[0])
.ThenBy(item => item.SplitOrderNumbers[1])
.ThenBy(item => item.SplitOrderNumbers[2])
.Select(item => item.OriginalSourceItem);

Bonus point: unlike in your solution, you split your orders only once.

LINQ and a natural sort order

There is no built-in way to do this using the .NET framework but I would suggest that you read Natural Sorting in C# for a discussion on the topic and an open-source implementation.

Natural Sort for Datgridview

The code project article 22517 that you link to has the logic in there that you require, it just needs some tweaking for use with a DataGridView by creating an implementation of System.Collections.IComparer rather than System.Collections.Generic.Comparer

So if you create yourself a new class in your project something like this:

public class NaturalSortComparer : System.Collections.IComparer {

private System.Collections.Generic.Dictionary<string, string[]> table;

public NaturalSortComparer() {
table = new System.Collections.Generic.Dictionary<string, string[]>();
}

public void Dispose() {
table.Clear();
table = null;
}

public int Compare(object x, object y) {
System.Windows.Forms.DataGridViewRow DataGridViewRow1 = (System.Windows.Forms.DataGridViewRow)x;
System.Windows.Forms.DataGridViewRow DataGridViewRow2 = (System.Windows.Forms.DataGridViewRow)y;

string xStr = DataGridViewRow1.Cells["Column1"].Value.ToString();
string yStr = DataGridViewRow2.Cells["Column1"].Value.ToString();


if (xStr == yStr) {
return 0;
}
string[] x1, y1;
if (!table.TryGetValue(xStr, out x1)) {
x1 = System.Text.RegularExpressions.Regex.Split(xStr.Replace(" ", ""), "([0-9]+)");
table.Add(xStr, x1);
}
if (!table.TryGetValue(yStr, out y1)) {
y1 = System.Text.RegularExpressions.Regex.Split(yStr.Replace(" ", ""), "([0-9]+)");
table.Add(yStr, y1);
}

for (int i = 0; i < x1.Length && i < y1.Length; i++) {
if (x1[i] != y1[i]) {
return PartCompare(x1[i], y1[i]);
}
}
if (y1.Length > x1.Length) {
return 1;
} else if (x1.Length > y1.Length) {
return -1;
} else {
return 0;
}
}

private static int PartCompare(string left, string right) {
int x, y;
if (!int.TryParse(left, out x)) {
return left.CompareTo(right);
}

if (!int.TryParse(right, out y)) {
return left.CompareTo(right);
}

return x.CompareTo(y);
}
}

You can see in here that I have hard coded it to used a column named "Column1" as per your example, but you can change this to be more dynamic.

When you then sort your grid, you can pass in a new instance of this class you have just created, like this:

dataGridView1.Sort(new NaturalSortComparer());

Sorting ListFileInfo in Natural sorted order .

Here you go; a handy list extension for natural sorting:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Demo
{
// A List extension class for natural sorting.

public static class ListExt
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern int StrCmpLogicalW(string lhs, string rhs);

// Version for lists of any type.
public static void SortNatural<T>(this List<T> self, Func<T, string> stringSelector)
{
self.Sort((lhs, rhs) => StrCmpLogicalW(stringSelector(lhs), stringSelector(rhs)));
}

// Simpler version for List<string>
public static void SortNatural(this List<string> self)
{
self.Sort(StrCmpLogicalW);
}
}

// Demonstrate using the List extension.

public class Program
{
private static void Main(string[] args)
{
var names = new List<FileInfo>
{
new FileInfo("abc.jpg"),
new FileInfo("abc10.jpg"),
new FileInfo("abc100.jpg"),
new FileInfo("abc98.jpg"),
new FileInfo("abc97.jpg"),
new FileInfo("abc102.jpg"),
new FileInfo("abc101.jpg")
};

names.SortNatural(x => x.Name);

foreach (var name in names)
Console.WriteLine(name);
}
}
}

The output from this program is:

abc.jpg
abc10.jpg
abc97.jpg
abc98.jpg
abc100.jpg
abc101.jpg
abc102.jpg

This takes advantage of the Windows API StrCmpLogicalW() method which does a natural sort order comparison, and uses P/Invoke to call it.

Sorting ListString in C#

How about:

    list.Sort((x, y) =>
{
int ix, iy;
return int.TryParse(x, out ix) && int.TryParse(y, out iy)
? ix.CompareTo(iy) : string.Compare(x, y);
});


Related Topics



Leave a reply



Submit