Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Using LINQ and Extension Methods in C# to Sort Vectors and Two-Dimensional Arrays

, 30 Mar 2011 CPOL
This article explains and shows examples of using LINQ and extension methods for array sorting
Program.zip
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace TwoDimArraySort
{

    /// <summary>
    /// This class is used to modify the default sorting order
    /// </summary>
    /// <typeparam name="T">The type of objects to compare</typeparam>
    class Comparer<T> : IComparer<T>
    {
        public int Compare(T x, T y)
        {
            if (typeof(T) == typeof(int))
            {
                int ix = (int)(object)x;
                int iy = (int)(object)y;
                return ix.CompareTo(iy);                        // <0 => x is less than y, 0 => x equals y, >0 => x is greater than y.
            }
            else if (typeof(T) == typeof(string))
            {
                string sx = (string)(object)x;
                string sy = (string)(object)y;
                return String.CompareOrdinal(sx, sy);            // Use CompareOrdinal to be case-sensitive
            }
            else
                return 0;
        }
    }

    static class Program
    {

        static internal Dictionary<int, string> dict;

        static void Main(string[] args)
        {

            // Use this to sort numbers based on their spelling
            #region Dictionary definition (even numbers are capitalized)
            dict = new Dictionary<int, string>(10);
            dict.Add(0, "Zero");
            dict.Add(1, "one");
            dict.Add(2, "Two");
            dict.Add(3, "three");
            dict.Add(4, "Four");
            dict.Add(5, "five");
            dict.Add(6, "Six");
            dict.Add(7, "seven");
            dict.Add(8, "Eight");
            dict.Add(9, "nine");
            #endregion

            // Create a jagged array
            int[][] ja = new int[3][];
            ja[0] = new int[] { 9, 7, 3, 8, 0, 6, 1, 2 };
            ja[1] = new int[] { 8, 6, 4, 3 };
            ja[2] = new int[] { 6, 9 };

            // Create a vector
            int[] v = ja[0];

            v.Show("v", "\r\nInitial vector");
            // To order a sequence by the values of the elements themselves, specify the identity function (x => x)
            #region Initial vector output
            // *****
            // v[0] = 9,    v[1] = 7,    v[2] = 3,    v[3] = 8,    v[4] = 0,    v[5] = 6,    v[6] = 1,    v[7] = 2,    
            // *****
            #endregion
            v.OrderBy(x => x).ToArray().Show("ovi", "\r\nSorted by integer value");
            #region Sorted by integer value output
            // *****
            // ovi[0] = 0,    ovi[1] = 1,    ovi[2] = 2,    ovi[3] = 3,    ovi[4] = 6,    ovi[5] = 7,    ovi[6] = 8,    ovi[7] = 9,    
            // *****
            #endregion
            // Use the function F(x) to convert the number argument x to a string based on its spelling
            v.OrderBy<int, string>(x => F(x)).ToArray().Show("ovci", "\r\nCase insensitive ordering of number's spelling");
            #region Case insensitive ordering of number's spelling output
            // *****
            // ovci[0] = 8,    ovci[1] = 9,    ovci[2] = 1,    ovci[3] = 7,    ovci[4] = 6,    ovci[5] = 3,    ovci[6] = 2,    ovci[7] = 0,    
            // *****
            #endregion
            // Use the string version of the generic Comparer class to order the results in case sensitive order
            v.OrderBy<int, string>(x => F(x), new Comparer<string>()).ToArray().Show("ovcs", "\r\nCase sensitive ordering -- even numbers first");
            #region Case sensitive ordering -- even numbers first output
            // *****
            // ovcs[0] = 8,    ovcs[1] = 6,    ovcs[2] = 2,    ovcs[3] = 0,    ovcs[4] = 9,    ovcs[5] = 1,    ovcs[6] = 7,    ovcs[7] = 3,    
            // *****
            #endregion

            ja.Show("ja", -999, "\r\n\r\nInitial jagged array");
            #region Initial jagged array output
            // *****
            // ja[0][0] = 9,    ja[0][1] = 7,    ja[0][2] = 3,    ja[0][3] = 8,    ja[0][4] = 0,    ja[0][5] = 6,    ja[0][6] = 1,    ja[0][7] = 2,    
            // ja[1][0] = 8,    ja[1][1] = 6,    ja[1][2] = 4,    ja[1][3] = 3,    
            // ja[2][0] = 6,    ja[2][1] = 9,    
            // *****
            #endregion
            // EnumerateInnerVectors returns an enumeration of the vectors representing the "rows" of the jagged array
            // Argument 1 specifies to pad each row to 8 elements with a value of -999 (representing a missing value)
            // The lambda expression x => x[1] orders the enumerated vectors based on the second column value
            int[][] oa = ja.EnumerateInnerVectors(8, -999).OrderByDescending(x => x[1]).ToArray();
            oa.Show("oja1d", -999, "\r\nJagged array ordered in descending order on the second column");
            #region Jagged array ordered in descending order on the second column output
            // *****
            // oja1d[0][0] = 6,    oja1d[0][1] = 9,    oja1d[0][2] = -,    oja1d[0][3] = -,    oja1d[0][4] = -,    oja1d[0][5] = -,    oja1d[0][6] = -,    oja1d[0][7] = -,    
            // oja1d[1][0] = 9,    oja1d[1][1] = 7,    oja1d[1][2] = 3,    oja1d[1][3] = 8,    oja1d[1][4] = 0,    oja1d[1][5] = 6,    oja1d[1][6] = 1,    oja1d[1][7] = 2,    
            // oja1d[2][0] = 8,    oja1d[2][1] = 6,    oja1d[2][2] = 4,    oja1d[2][3] = 3,    oja1d[2][4] = -,    oja1d[2][5] = -,    oja1d[2][6] = -,    oja1d[2][7] = -,    
            // *****
            #endregion

            // Order the rows based on the value of the fifth column of each vector
            ja.EnumerateInnerVectors(5, -999).OrderBy(x => x[4]).ToArray()
                .Show("oj4a", -999, "\r\nJagged array ordered in ascending order on the fifth column");
            #region Jagged array ordered in ascending order on the fifth column output
            // *****
            // oj4a[0][0] = 8,    oj4a[0][1] = 6,    oj4a[0][2] = 4,    oj4a[0][3] = 3,    oj4a[0][4] = -,    
            // oj4a[1][0] = 6,    oj4a[1][1] = 9,    oj4a[1][2] = -,    oj4a[1][3] = -,    oj4a[1][4] = -,    
            // oj4a[2][0] = 9,    oj4a[2][1] = 7,    oj4a[2][2] = 3,    oj4a[2][3] = 8,    oj4a[2][4] = 0,    oj4a[2][5] = 6,    oj4a[2][6] = 1,    oj4a[2][7] = 2,    
            // *****
            #endregion

            // Sorting a two-dimensional array
            int[,] array = new int[3, 3] { { 9, 1, 4 }, { 4, 5, 1 }, { 7, 3, 8 } };
            array.Show("array", "\r\nInitial 2 dimensional array");
            #region Initial 2 dimensional array output
            // *****
            // array[0][0] = 9,    array[0][1] = 1,    array[0][2] = 4,    
            // array[1][0] = 4,    array[1][1] = 5,    array[1][2] = 1,    
            // array[2][0] = 7,    array[2][1] = 3,    array[2][2] = 8,    
            // *****
            #endregion

            int[,] sortedByFirstElement = array.OrderBy(x => x[0]);
            sortedByFirstElement.Show("array", "\r\nArray sorted by first element");
            #region Array sorted by first element output
            // *****
            // array[0][0] = 4,    array[0][1] = 5,    array[0][2] = 1,    
            // array[1][0] = 7,    array[1][1] = 3,    array[1][2] = 8,    
            // array[2][0] = 9,    array[2][1] = 1,    array[2][2] = 4,    
            // *****
            #endregion
            int[,] sortedBySecondElement = array.OrderBy(x => x[1]);
            sortedBySecondElement.Show("array", "\r\nArray sorted by second element");
            #region Array sorted by second element output
            // *****
            // array[0][0] = 9,    array[0][1] = 1,    array[0][2] = 4,    
            // array[1][0] = 7,    array[1][1] = 3,    array[1][2] = 8,    
            // array[2][0] = 4,    array[2][1] = 5,    array[2][2] = 1,    
            // *****
            #endregion
            int[,] sortedByThirdElement = array.OrderBy(x => x[2]);
            sortedByThirdElement.Show("array", "\r\nArray sorted by third element");
            #region Array sorted by third element output
            // *****
            // array[0][0] = 4,    array[0][1] = 5,    array[0][2] = 1,    
            // array[1][0] = 9,    array[1][1] = 1,    array[1][2] = 4,    
            // array[2][0] = 7,    array[2][1] = 3,    array[2][2] = 8,    
            // *****
            #endregion
        }

        /// <summary>
        /// This function is used to sort numbers from 0-9 based on their spelling
        /// </summary>
        /// <param name="n">The input number</param>
        /// <returns>The spelling of the number</returns>
        static string F(int n)
        {
            return dict[n];         // dict[0] = "Zero", dict[1] = "one", dict[2] = "Two", etc.
        }

        /// <summary>
        /// This extension method is used to display a vector
        /// </summary>
        /// <param name="array">The vector to display</param>
        /// <param name="name">The name of the vector, e.g., if "v" is the name the
        /// display will be v[0] = n1, v[1] = n2, ...</param>
        /// <param name="title">Optional text to describe the output.</param>
        /// <param name="width">The minimum width of each output column.</param>
        static void Show(this int[] array, string name, string title = "", int width = 6)
        {
            Debug.WriteLine(title + "\r\n*****");
            for (int i = 0; i <= array.GetUpperBound(0); i++)
                Debug.Write(name + "[" + i.ToString() + "] = " + (array[i].ToString() + ", ").PadRight(width));
            Debug.WriteLine("\r\n*****");
        }

        /// <summary>
        /// This extension method is used to display a two-dimensional jagged array
        /// </summary>
        /// <param name="array">The jagged array to display</param>
        /// <param name="name">The name of the array, e.g., if "ja" is the name the
        /// display will be ja[0][0] = n1, ja[0][1] = n2, ...</param>
        /// <param name="missingValue">An integer value indicating missing data in a jagged array
        /// which will be replaced by a special character.</param>
        /// <param name="title">Optional text to describe the output.</param>
        /// <param name="width">The minimum width of each output column.</param>
        static void Show(this int[][] array, string name, int missingValue, string title = "", int width = 6)
        {
            Debug.WriteLine(title + "\r\n*****");
            for (int i = 0; i <= array.GetUpperBound(0); i++)
            {
                for (int j = 0; j <= array[i].GetUpperBound(0); j++)
                    Debug.Write(name + "[" + i.ToString() + "][" + j.ToString() + "] = " + (array[i][j].AsString(missingValue) + ", ").PadRight(width));
                Debug.WriteLine("");
            }
            Debug.WriteLine("*****");
        }

        /// <summary>
        /// This extension method is used to display a two-dimensional array
        /// </summary>
        /// <param name="array">The array to display</param>
        /// <param name="name">The name of the array, e.g., if "a" is the name the
        /// display will be a[0,0] = n1, a[0,1] = n2, ...</param>
        /// <param name="title">Optional text to describe the output.</param>
        /// <param name="width">The minimum width of each output column.</param>
        static void Show(this int[,] array, string name, string title = "", int width = 6)
        {
            Debug.WriteLine(title + "\r\n*****");
            for (int i = 0; i <= array.GetUpperBound(0); i++)
            {
                for (int j = 0; j <= array.GetUpperBound(1); j++)
                    Debug.Write(name + "[" + i.ToString() + "][" + j.ToString() + "] = " + (array[i, j].ToString() + ", ").PadRight(width));
                Debug.WriteLine("");
            }

            Debug.WriteLine("*****");
        }

        /// <summary>
        /// This extension method is used to convert an integer to a string replacing "missing values"
        /// (arbitrarily) to a minus sign.
        /// </summary>
        /// <param name="n">The integer value to convert.</param>
        /// <param name="missingValue">The value to be replaced by the minus sign.</param>
        /// <returns>The string value of the number or a minus sign.</returns>
        static string AsString(this int n, int missingValue)
        {
            if (n == missingValue)
                return "-";
            else
                return n.ToString();
        }

        /// <summary>
        /// Enumerates the "inner" vectors of a two dimensional jagged array. 
        /// </summary>
        /// <typeparam name="T">The type of the jagged two-dimensional array.</typeparam>
        /// <param name="source">The jagged two-dimensional array.</param>
        /// <returns>An enumeration of T[] consisting of the inner vectors of the jagged array.</returns>
        /// <remarks>Note that if this is used with OrderBy, the order-by column must exist in all inner vectors.</remarks>
        private static IEnumerable<T[]> EnumerateInnerVectors<T>(this T[][] source)
        {
            for (int i = 0; i <= source.GetUpperBound(0); i++)
                yield return source[i];
        }

        /// <summary>
        /// Enumerates the "inner" vectors of a two dimensional jagged array.  Short vectors will be padded to a specified minimum length.
        /// </summary>
        /// <typeparam name="T">The type of the jagged two-dimensional array.</typeparam>
        /// <param name="source">The jagged two-dimensional array.</param>
        /// <param name="minLength">The minimum length of an inner vector.  Rows will be padded with the default value to this length.</param>
        /// <param name="missingValue">A value of type T to be used for short rows.</param>
        /// <returns>An enumeration of T[] consisting of the inner vectors of the jagged array padded with missingValue values if necessary.</returns>
        private static IEnumerable<T[]> EnumerateInnerVectors<T>(this T[][] source, int minLength, T missingValue)
        {
            for (int i = 0; i <= source.GetUpperBound(0); i++)
            {
                if (source[i].Length >= minLength)
                {
                    yield return source[i];
                    continue;
                }

                T[] v = new T[minLength];
                for (int j = 0; j < minLength; j++)
                {
                    if (j > source[i].Length - 1)
                        v[j] = missingValue;
                    else
                        v[j] = source[i][j];
                }
                yield return v;
            }
        }


        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // The following code was written by Andrew Rissing (http://www.codeproject.com/script/Membership/View.aspx?mid=1431085) // 
        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Orders the two dimensional array by the provided key in the key selector.
        /// </summary>
        /// <typeparam name="T">The type of the source two-dimensional array.</typeparam>
        /// <param name="source">The source two-dimensional array.</param>
        /// <param name="keySelector">The selector to retrieve the column to sort on.</param>
        /// <returns>A new two dimensional array sorted on the key.</returns>
        public static T[,] OrderBy<T>(this T[,] source, Func<T[], T> keySelector)
        {
            return source.ConvertToSingleDimension().OrderBy(keySelector).ConvertToMultiDimensional();
        }

        /// <summary>
        ///   Orders the two dimensional array by the provided key in the key selector in descending order.
        /// </summary>
        /// <typeparam name="T">The type of the source two-dimensional array.</typeparam>
        /// <param name="source">The source two-dimensional array.</param>
        /// <param name="keySelector">The selector to retrieve the column to sort on.</param>
        /// <returns>A new two dimensional array sorted on the key.</returns>
        public static T[,] OrderByDescending<T>(this T[,] source, Func<T[], T> keySelector)
        {
            return source.ConvertToSingleDimension().OrderByDescending(keySelector).ToArray().ConvertToMultiDimensional();
        }

        /// <summary>
        ///   Converts a two dimensional array to single dimensional array.
        /// </summary>
        /// <typeparam name="T">The type of the two dimensional array.</typeparam>
        /// <param name="source">The source two dimensional array.</param>
        /// <returns>The repackaged two dimensional array as a single dimension based on rows.</returns>
        private static IEnumerable<T[]> ConvertToSingleDimension<T>(this T[,] source)
        {
            T[] arRow;
            for (int row = 0; row < source.GetLength(0); ++row)
            {
                arRow = new T[source.GetLength(1)];
                for (int col = 0; col < source.GetLength(1); ++col)
                    arRow[col] = source[row, col];
                yield return arRow;
            }
        }

        /// <summary>
        ///   Converts a collection of rows from a two dimensional array back into a two dimensional array.
        /// </summary>
        /// <typeparam name="T">The type of the two dimensional array.</typeparam>
        /// <param name="source">The source collection of rows to convert.</param>
        /// <returns>The two dimensional array.</returns>
        private static T[,] ConvertToMultiDimensional<T>(this IEnumerable<T[]> source)
        {
            T[,] twoDimensional;
            T[][] arrayOfArray;
            int numberofColumns;
            arrayOfArray = source.ToArray();
            numberofColumns = (arrayOfArray.Length > 0) ? arrayOfArray[0].Length : 0;
            twoDimensional = new T[arrayOfArray.Length, numberofColumns];
            for (int row = 0; row < arrayOfArray.GetLength(0); ++row)
                for (int col = 0; col < numberofColumns; ++col)
                    twoDimensional[row, col] = arrayOfArray[row][col];
            return twoDimensional;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Tony Zackin
Software Developer (Senior) Takamomto, LLC
United States United States
I have been doing database related programming in the financial services industry for over 20 years on various platforms. I used to be a UNIX Sybase DBA but now prefer programming in the .NET environment using C# and SQL Server.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141220.1 | Last Updated 30 Mar 2011
Article Copyright 2011 by Tony Zackin
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid