-5

C#, How to sort DataTable in a customised order?

I have a DataTable that is already filled with data, how do I sort it in a customised order?

For example, I have a column in the DataTable called Animals with the following values:

Cat, Cat, Bird, Bird, Dog, Dog, Hamster, Hamster

I want to sort it in a customised order where they are ordered in ascending order of Hamster, Bird, Cat, Dog.

So my output based on my above example should be:

Hamster, Hamster, Bird, Bird, Cat, Cat, Dog, Dog

What would be the recommended way to do it?

John Smith
  • 1
  • 1
  • 3
  • 3
    what does the Sql query look like where you are populating the DataTable.. have you tried writing a query, running it and refactoring the query where necessary to get the results that you are expecting..? `Select ColumnName From Table Order by ColumnName Desc` please show more effort also google on how to write basic SQL this is not that difficult in nature – MethodMan Oct 07 '14 at 17:02
  • John do you have `SQL Statement` that you can share with the rest of us.. ? – MethodMan Oct 07 '14 at 17:09
  • I am unable to do anything about the SQL side due to company policy. I have to work with solely with the populated DataTable. – John Smith Oct 07 '14 at 17:21

3 Answers3

2

As I didn't read the question, apologies, this is a nasty way of doing it, but should work.

DataTable dt =  YOURTABLE.Select("Animals == 'Hamster'").CopyToDataTable();
DataTable dt2 = YOURTABLE.Select("Animals != 'Hamster'").CopyToDataTable();

dt2 = dt2.Sort = "Animals + " " + "Asc";
dt.Merge(dt2);
YOURTABLE = dt;

Not tested.

Ben Temple-Heald
  • 708
  • 1
  • 6
  • 16
0

While I am having difficulty finding any documentation to this effect, it appears as though the DataTable class itself is order agnostic - that is to say, it will present the records in the sequence in which they were loaded (in the case of a DataTable loaded through an adapter, this would be the order of the rows in the result set).

It is possible to extract the records in a specific sort order (as seen here: Sorting rows in a data table) and create a new DataTable with the rows in this new sequence. This seems to be how most people gain the effect of a sort.

However the Select method accepts its sorting condition as a string (http://msdn.microsoft.com/en-us/library/way3dy9w(v=vs.110).aspx) which means that the sort conditions are limited to those supported by the class. All the documentation indicates to be supported is a column name and a direction.

Since what you want is a custom sortation, not a basic one by column, it would appear that the base DataTable has no mechanism for handling this built-in. What I would expect to be necessary in your scenario is to write some code to extract the records from the DataTable, sort them with a custom sorter (using LINQ's OrderBy with a function on the extracted data would probably do the trick), and then insert them into a new DataTable that your code uses going forward.

As an example of this approach:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            DataTable inputDataTable = CreateInputDataTable();
            Console.WriteLine("Input data table: ");
            PrintDataTable(inputDataTable);
            DataTable outputDataTable = CustomSortDataTable(inputDataTable);
            Console.WriteLine("Sorted data table: ");
            PrintDataTable(outputDataTable);
        }

        private static DataTable CustomSortDataTable(DataTable inputDataTable)
        {
            DataRow[] rows = inputDataTable.Select();
            IComparer<string> animalTypeComparer = new AnimalTypeComparer();

            IEnumerable<DataRow> sortedRows = rows.OrderBy(x => x["AnimalType"].ToString(), animalTypeComparer);

            DataTable result = new DataTable();

            result.Columns.Add("ID");
            result.Columns.Add("AnimalType");

            foreach(DataRow row in sortedRows)
            {
                result.ImportRow(row);
            }

            return result;
        }

        private static void PrintDataTable(DataTable inputDataTable)
        {
            foreach(DataRow row in inputDataTable.Rows)
            {
                Console.WriteLine("({0}, {1})", row["ID"], row["AnimalType"]);
            }
        }

        private static DataTable CreateInputDataTable()
        {
            DataTable result = new DataTable();

            result.Columns.Add("ID");
            result.Columns.Add("AnimalType");

            DataRow toInsert = result.NewRow();

            toInsert["ID"] = 1;
            toInsert["AnimalType"] = "Cat";
            result.Rows.Add(toInsert);

            toInsert = result.NewRow();
            toInsert["ID"] = 2;
            toInsert["AnimalType"] = "Cat";
            result.Rows.Add(toInsert);

            toInsert = result.NewRow();
            toInsert["ID"] = 3;
            toInsert["AnimalType"] = "Bird";
            result.Rows.Add(toInsert);

            toInsert = result.NewRow();
            toInsert["ID"] = 4;
            toInsert["AnimalType"] = "Bird";
            result.Rows.Add(toInsert);

            toInsert = result.NewRow();
            toInsert["ID"] = 5;
            toInsert["AnimalType"] = "Dog";
            result.Rows.Add(toInsert);

            toInsert = result.NewRow();
            toInsert["ID"] = 6;
            toInsert["AnimalType"] = "Dog";
            result.Rows.Add(toInsert);

            toInsert = result.NewRow();
            toInsert["ID"] = 7;
            toInsert["AnimalType"] = "Hamster";
            result.Rows.Add(toInsert);

            toInsert = result.NewRow();
            toInsert["ID"] = 8;
            toInsert["AnimalType"] = "Hamster";
            result.Rows.Add(toInsert);

            return result;
        }
    }

    class AnimalTypeComparer : IComparer<string>
    {
        private static readonly string[] AnimalTypes = {"Hamster", "Bird", "Cat", "Dog"};
        #region Implementation of IComparer<in string>

        /// <summary>
        /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
        /// </summary>
        /// <returns>
        /// A signed integer that indicates the relative values of <paramref name="x"/> and <paramref name="y"/>, as shown in the following table.Value Meaning Less than zero<paramref name="x"/> is less than <paramref name="y"/>.Zero<paramref name="x"/> equals <paramref name="y"/>.Greater than zero<paramref name="x"/> is greater than <paramref name="y"/>.
        /// </returns>
        /// <param name="x">The first object to compare.</param><param name="y">The second object to compare.</param>
        public int Compare(string x, string y)
        {
            return Array.IndexOf(AnimalTypes, x).CompareTo(Array.IndexOf(AnimalTypes, y));
        }

        #endregion
    }
}

Running this prints out the following:

Input data table:
(1, Cat)
(2, Cat)
(3, Bird)
(4, Bird)
(5, Dog)
(6, Dog)
(7, Hamster)
(8, Hamster)
Sorted data table:
(7, Hamster)
(8, Hamster)
(3, Bird)
(4, Bird)
(1, Cat)
(2, Cat)
(5, Dog)
(6, Dog)
Community
  • 1
  • 1
Michael
  • 1,306
  • 1
  • 12
  • 30
0

I do have an unorthodox solution to my problem which is by adding a new column which stores a number corresponding to the sort order I require.

Example:

Animals: Cat, Cat, Bird, Bird, Dog, Dog, Hamster SortNumber: 3, 3, 2, 2, 4, 4, 1, 1

Which is the possibly the simplest way to do it. But I was hoping there is a more "proper" solution out there.

John Smith
  • 1
  • 1
  • 3