2

I have a requirement to sort the values of a data table column. That column contains strings, integers or mixed texts. For example:

The data table column contains values like this: 23, 18, 12, store 23, store a1, 1283, 25, ...

If I sort the values by using Dataview.sort() method it results in this order: 12, 1283, 18, 23, 25, store 1283, store a1, ... but I need like this: 12, 18, 23, 25, 1283, store 23, store a1, ...

Is there any simple method for attaining this requirement?

ChrisWue
  • 18,612
  • 4
  • 58
  • 83
MAC
  • 6,277
  • 19
  • 66
  • 111

4 Answers4

6

I think you should use natural sorting and make your own IComparer

The best algo I found was here

http://www.davekoelle.com/files/AlphanumComparator.cs.

Just make it a generic class(as linq uses as Linq order by takes IComparer) , like following

public class AlphanumComparator<T> : IComparer<T>
    {
        private enum ChunkType { Alphanumeric, Numeric };
        private bool InChunk(char ch, char otherCh)
        {
            ChunkType type = ChunkType.Alphanumeric;

            if (char.IsDigit(otherCh))
            {
                type = ChunkType.Numeric;
            }

            if ((type == ChunkType.Alphanumeric && char.IsDigit(ch))
                || (type == ChunkType.Numeric && !char.IsDigit(ch)))
            {
                return false;
            }

            return true;
        }

        public int Compare(T x, T y)
        {
            String s1 = x as string;
            String s2 = y as string;
            if (s1 == null || s2 == null)
            {
                return 0;
            }

            int thisMarker = 0, thisNumericChunk = 0;
            int thatMarker = 0, thatNumericChunk = 0;

            while ((thisMarker < s1.Length) || (thatMarker < s2.Length))
            {
                if (thisMarker >= s1.Length)
                {
                    return -1;
                }
                else if (thatMarker >= s2.Length)
                {
                    return 1;
                }
                char thisCh = s1[thisMarker];
                char thatCh = s2[thatMarker];

                StringBuilder thisChunk = new StringBuilder();
                StringBuilder thatChunk = new StringBuilder();

                while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || InChunk(thisCh, thisChunk[0])))
                {
                    thisChunk.Append(thisCh);
                    thisMarker++;

                    if (thisMarker < s1.Length)
                    {
                        thisCh = s1[thisMarker];
                    }
                }

                while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || InChunk(thatCh, thatChunk[0])))
                {
                    thatChunk.Append(thatCh);
                    thatMarker++;

                    if (thatMarker < s2.Length)
                    {
                        thatCh = s2[thatMarker];
                    }
                }

                int result = 0;
                // If both chunks contain numeric characters, sort them numerically
                if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0]))
                {
                    thisNumericChunk = Convert.ToInt32(thisChunk.ToString());
                    thatNumericChunk = Convert.ToInt32(thatChunk.ToString());

                    if (thisNumericChunk < thatNumericChunk)
                    {
                        result = -1;
                    }

                    if (thisNumericChunk > thatNumericChunk)
                    {
                        result = 1;
                    }
                }
                else
                {
                    result = thisChunk.ToString().CompareTo(thatChunk.ToString());
                }

                if (result != 0)
                {
                    return result;
                }
            }

            return 0;
        }


    }

Now to apply this, use linq

 DataTable dt = new DataTable();
            dt.TableName = "Sort";
            dt.Columns.Add("Check");
            DataRow dr = dt.NewRow();
            dr["Check"] = "12";
            dt.Rows.Add(dr);

            DataRow dr2 = dt.NewRow();
            dr2["Check"] = "1283";
            dt.Rows.Add(dr2);

            DataRow dr3 = dt.NewRow();
            dr3["Check"] = "store 1283";
            dt.Rows.Add(dr3);

            DataRow dr4 = dt.NewRow();
            dr4["Check"] = "23";
            dt.Rows.Add(dr4);

            DataView dv = new DataView();
            dv.Table = dt;

            AlphanumComparator<string> comparer = new AlphanumComparator<string>();
            //DataTable dtNew = dv.Table;
            DataTable dtNew = dv.Table.AsEnumerable().OrderBy(x => x.Field<string>("Check"), comparer).CopyToDataTable();
            dtNew.TableName = "NaturalSort";

            dv.Table = dtNew;

Result 12, 23, 1283, store 1283

Anand
  • 14,545
  • 8
  • 32
  • 44
  • This version and the version on the original site has a bug when the numerical chunk overflows Int32. The original approach of the algorithm is to simply compare numerical chunks char by char until one is different. The bug is to `Convert.ToInt32(thisChunk.ToString());` – Joan Charmant Sep 09 '12 at 17:53
1

You can't directly according to your custom criteria. You will have to write your own comparison code

Take a look at this Question

Community
  • 1
  • 1
Haris Hasan
  • 29,856
  • 10
  • 92
  • 122
1

What is the datatype of the column. Data you posted is like alphanumeric i.e., varchar

You can sort the data in the datatable by using this line of code. Try this once.

datatable.DefaultView.Sort = "COLUMN_NAME ASC"; 

If not Can you just rephrase your question specifying the datatype of the column because the column has both alphanumeric and numeric values.

Michał Powaga
  • 22,561
  • 8
  • 51
  • 62
JayOnDotNet
  • 398
  • 1
  • 5
  • 17
1

The standard DB-level or DataView-type sort doesn't support mixed-type comparisons.

You could copy the rows from the original DataTable into an Array (such as with DataTable.Rows.CopyTo(), and then call Array.Sort() with a custom comparator.

RickNZ
  • 18,448
  • 3
  • 51
  • 66