Introduction
Almost every program I have created needs a method for sorting a two-dimensional array by column one in the selected sort order, then by column two in the selected sort order, and then by column three in the selected sort order ... then by column n in the selected sort order.
Sort two-dimensional arrays
For sorting two-dimensional arrays containing different types of values, integers, strings, booleans, etc., I have used the DataGridView class and its DataGridViewSort event handler. It is easy to manipulate data in any way and offers fast sorting as well. An article about that can be found here.
But sometimes, it is not possible to implement such a solution. So, I have made the decision to try to write my own universal method for sorting 2D arrays that could be used for sorting arrays in any future program that I am going to write.
After a while, I have written this method.
/// <summary>
/// Sorts a two-dimensional array of strings by selected columns in selected sort order using the Selection Sort Algorithm.
/// </summary>
/// <param name="array">
/// Two-dimensional array of strings containing values that are going to be sorted.
/// </param>
/// <param name="sort_directive">
/// Two-dimensional array of integers. First column contains the order of the column indexes
/// by which the array will be sorted. Second column contains column sort order.
/// Sort order for each column is defined by integer values:
/// -1 - sort column values in Descending sort order
/// 0 - column is not going to be sorted
/// 1 - sort column values in Ascending sort order
/// </param>
/// <returns>
/// Sorted array by selected columns in selected sort order.
/// </returns>
public string[,] SortStringArray(string[,] array, int[,] sort_directive)
{
int arrayRows = array.GetLength(0);
int arrayColumns = array.Length / arrayRows;
int sortDirectiveColumns = sort_directive.GetLength(0);
for (int i = 0; i < arrayRows - 1; i++)
{
for (int j = i + 1; j < arrayRows; j++)
{
for (int c = 0; c < sortDirectiveColumns; c++)
{
int columnToSort = sort_directive[c, 0];
int sortOrder = sort_directive[c, 1];
int comparisonResult = array[i, columnToSort].CompareTo(array[j, columnToSort]);
if ((sortOrder == -1 && comparisonResult < 0) ||
(sortOrder == 1 && comparisonResult > 0))
{
for (int d = 0; d < arrayColumns; d++)
{
string temp = array[j, d];
array[j, d] = array[i, d];
array[i, d] = temp;
}
break;
}
else if ((sortOrder == -1 && comparisonResult > 0) ||
(sortOrder == 1 && comparisonResult < 0))
{
break;
}
}
}
}
return array;
}
How did I develop the Sort method?
The first decision was that the 2D array should strictly be the type of string.
Since any other type of data (int, double, float, decimal, char, boolean, ...) can be represented as a string value, and because of that, the 2D array type of string could contain diverse types of data that could be easily manipulated and sorted.
Important
Data stored inside the array type of string are always represented (memorized) as a type of String. When sorting, all data are compared as strings and numbers. If there is a need to sort numbers, it is necessary to adjust the format of data so that the sort routine returns the correct result.
numerical data as strings |
numerical data formated for sorting as number values |
sorted correctly as numbers |
"1" |
"0001" |
"0001" |
"10" |
"0010" |
"0003" |
"214" |
"0214" |
"0004" |
"3" |
"0003" |
"0005" |
"4" |
"0004" |
"0010" |
"5" |
"0005" |
"0214" |
"1024" |
"1024" |
"1024" |
numerical data as strings |
numerical data not formated for sorting as number values |
sorted correctly as strings |
"1" |
"1" |
"1" |
"10" |
"10" |
"10" |
"214" |
"214" |
"1024" |
"3" |
"3" |
"214" |
"4" |
"4" |
"3" |
"5" |
"5" |
"4" |
"1024" |
"1024" |
"5" |
Example of formatting numerical data.
int numerical_data = 100;
string format = "000000";
string formated_data = numerical_data.ToString(format);
Result of formatting.
Numerical data |
Formated data |
100 |
000100 |
1000 |
001000 |
10000 |
010000 |
The name of the 2D array inside the program shall be TABLE.
Declaration of TABLE with 10 rows and 10 columns, max 100 elements.
int rows = 10;
int columns = 10;
string [,] TABLE = new string[rows,columns];
The second decision was to use a 2D array type of integer for storing TABLE sort directives.
A simple sort directive looks like this.
Sort TABLE by Column One in Ascending sort order.
As shown, the sort directive consists of the selected TABLE column index and its sort order.
So this array should have only two columns.
The first column shall contain a selected column index.
The second column contains column sort order defined by integer values.
- - 1: Sort column values in Descending sort order
- 0: The column is not going to be sorted
- 1: Sort column values in Ascending sort order
The number of rows depends on how many sort directives are entered by the user.
The name of the 2D array inside the program shall be SORT_DIRECTIVE.
Declaration of SORT_DIRECTIVE,
int rows = number of sort directives;
int columns = 2;
int [,] SORT_DIRECTIVE = new string[rows,columns];
Example of sort directive and SORT_DIRECTIVE element values
TABLE has seven rows and three columns,
- Sort TABLE by Column One in Ascending sort order, then by
- Column Two in Descending sort order then by
- Column Three in Ascending sort order.
SORT_DIRECTIVE
Column Index |
Sort Order |
1 |
1 |
2 |
-1 |
3 |
1 |
The third and final decision was the selection of the Sort Algorithm.
I have decided to use the Selection Sort Algorithm for its simplicity of implementation. Also, it is fast enough for a small amount of data, up to 1000 records.
How does SORT_DIRECTIVE work?
Create and fill the TABLE with data. Be careful if some column should contain numbers. Numbers must be specially formatted for sorting as described in the important notice above.
// Create and fill TABLE
string[,] TABLE = new string[7, 3]
{
{ "John", "Porter", "1234 Oak street, Pensacola, FL 32503" },
{ "John", "Porter", "1891 3rd Street North Westlake, OH 44145" },
{ "William", "Patterson", "4534 Virginia Street Dallas, GA 30132" },
{ "Marry", "Cortez", "7642 Fairview Avenue Milwaukee, WI 53204" },
{ "John", "Patterson", "1368 Street Road Morristown, NJ 07960" },
{ "Elizabet", "Cortez", "3698 Cedar Avenue Saratoga Springs, NY 12886" },
{ "Marry", "Mosley", "4575 11th Street Sacramento, CA 95820" }
};
Then, create and fill SORT_DIRECTIVE.
//
// Table SORT DIRECTIVE contains order of columns
// by which array is going to be sorted and sort order
// for each selected column
//
// 0 - do not sort column
// -1 - sort in Descending order
// 1 - sort in Ascending order
//
int [,] SORT_DIRECTIVE=new int[3,2]
{
{0, 1},
{1, -1},
{2, 1}
};
Call method.
//
// Call sort method
//
TABLE = Sort_String(TABLE,SORT_DIRECTIVE);
When finished, the method returns a sorted array TABLE by values inside SORT_DIRECTIVE.
//
// Print sorted TABLE
//
Console.WriteLine();
Console.WriteLine(" SORTED TABLE");
Console.WriteLine(" --------------");
Console.WriteLine();
for(int n=0;n<7;n++)
{
Console.WriteLine(" {0,-10}{1,-10}{2,-10}",TABLE[n,0],TABLE[n,1],TABLE[n,2]);
}
Console.WriteLine(" --------------");
//
Here is the program's screenshot.
Note. The names and addresses shown in the picture are fictional.
Conclusion
For sorting up to a thousand rows filled with data, this method is almost as fast as DataGridView with its SortEventHandler. If there is a need for sorting more data than that, perhaps by implementing a faster sort algorithm, things would be better. This method can easily be changed to sort 2D arrays of any type. Inside open source solution, there is a class Sort_2D_Array containing these methods,
- Sort_String
- Sort_Int
- Sort_Double
- Sort_Decimal
- Sort_Float
The name of the method shows what is the type of array that is going to be sorted.
The download link for open-source solutions is at the beginning of this article. The archive is called TAS.zip ( two-dimensional array sort ). The solution was created in IDE SharpDevelop and needs .NET 4.0 to work correctly.
Updated on March 8, 2024
Instead of method here is a C# extension class, SortExtensions
, designed to extend the sorting capabilities of two-dimensional arrays using the Selection sort algorithm.To sort a two-dimensional array, the SelectionSortArray
method is exposed as a public static method, which can be called on any two-dimensional array. It requires the array to be sorted and a two-dimensional integer array (sort_directive
) specifying the sorting directives.
public static void SelectionSortArray<T>(this T[,] array, int[,] sort_directive)
where T : IComparable<T>
The T
type parameter indicates that the method can sort arrays of any type that implements the IComparable<T>
interface, making it versatile for various data types.
Using SortExtensions
to sort a two-dimensional array involves just a few steps:
Since it's an extension method, you can call it directly on the array object, passing the sorting directives as the second argument.
//
// Call sort method
//
TABLE.QuickSortArray(SORT_DIRECTIVE);
/// <summary>
/// Provides extension methods for sorting two-dimensional arrays
/// using the Selection Sort algorithms.
/// </summary>
public static class SortExtensions
{
/// <summary>
/// Sorts a two-dimensional array
/// based on sort directives using the Selection Sort algorithm.
/// </summary>
/// <typeparam name="T">The type of elements in the array. Must be comparable.</typeparam>
/// <param name="array">The two-dimensional array to be sorted.</param>
/// <param name="sort_directive">A two-dimensional integer array
/// where the first column specifies the column index to sort by,
/// and the second column specifies the sort order
/// (1 for ascending, -1 for descending).</param>
public static void SelectionSortArray<T>(this T[,] array, int[,] sort_directive)
where T : IComparable<T>
{
int arrayRows = array.GetLength(0);
int arrayColumns = array.Length / arrayRows;
int sortDirectiveColumns = sort_directive.GetLength(0);
for (int i = 0; i < arrayRows - 1; i++)
{
int minIndex = i;
for (int j = i + 1; j < arrayRows; j++)
{
bool shouldSwap = false;
for (int c = 0; c < sortDirectiveColumns; c++)
{
int columnToSort = sort_directive[c, 0];
int sortOrder = sort_directive[c, 1];
T valueI = array[minIndex, columnToSort];
T valueJ = array[j, columnToSort];
int comparisonResult = valueI.CompareTo(valueJ);
if ((sortOrder == -1 && comparisonResult < 0) ||
(sortOrder == 1 && comparisonResult > 0))
{
shouldSwap = true;
break;
}
else if ((sortOrder == -1 && comparisonResult > 0) ||
(sortOrder == 1 && comparisonResult < 0))
{
break;
}
}
if (shouldSwap)
{
minIndex = j;
}
}
if (minIndex != i)
{
for (int d = 0; d < arrayColumns; d++)
{
T temp = array[minIndex, d];
array[minIndex, d] = array[i, d];
array[i, d] = temp;
}
}
}
}
}