web-dev-qa-db-ja.com

C#で2次元(長方形)配列をソートするにはどうすればよいですか?

データテーブル(行と列)を構成する(文字列の)2次元配列があります。この配列を任意の列で並べ替えます。 C#でこれを行うためのアルゴリズムを見つけようとしましたが、成功していません。

どんな助けも大歓迎です。

33
Jack

2次元の文字列配列を実際のDataTable(System.Data.DataTable)に読み込み、DataTableオブジェクトのSelect()メソッドを使用して、ソートされたDataRowオブジェクトの配列を生成します(または同様の効果を得るためにDataViewを使用します)。

// assumes stringdata[row, col] is your 2D string array
DataTable dt = new DataTable();
// assumes first row contains column names:
for (int col = 0; col < stringdata.GetLength(1); col++)
{
    dt.Columns.Add(stringdata[0, col]);
}
// load data from string array to data table:
for (rowindex = 1; rowindex < stringdata.GetLength(0); rowindex++)
{
    DataRow row = dt.NewRow();
    for (int col = 0; col < stringdata.GetLength(1); col++)
    {
        row[col] = stringdata[rowindex, col];
    }
    dt.Rows.Add(row);
}
// sort by third column:
DataRow[] sortedrows = dt.Select("", "3");
// sort by column name, descending:
sortedrows = dt.Select("", "COLUMN3 DESC");

また、2次元配列をソートする独自のメソッドを作成することもできます。どちらのアプローチも学習に役立ちますが、DataTableアプローチを使用すると、C#アプリケーションでデータのテーブルをより適切に処理する方法を学ぶことができます。

25
MusiGenesis

確認できますか?長方形の配列([,])またはギザギザの配列([][])ですか?

ギザギザの配列を並べ替えることは非常に簡単です。それについての議論があります here 。この場合、明らかにComparison<T>には順序によるソートの代わりに列が含まれますが、非常に似ています。

長方形の配列を並べ替えるのは難しい...データを長方形の配列またはList<T[]>にコピーして、there、コピーバックします。

ギザギザの配列を使用した例を次に示します。

static void Main()
{  // could just as easily be string...
    int[][] data = new int[][] { 
        new int[] {1,2,3}, 
        new int[] {2,3,4}, 
        new int[] {2,4,1} 
    }; 
    Sort<int>(data, 2); 
} 
private static void Sort<T>(T[][] data, int col) 
{ 
    Comparer<T> comparer = Comparer<T>.Default;
    Array.Sort<T[]>(data, (x,y) => comparer.Compare(x[col],y[col])); 
} 

長方形の配列を操作するために...まあ、ここで2つをオンザフライで交換するコードがあります...

static T[][] ToJagged<T>(this T[,] array) {
    int height = array.GetLength(0), width = array.GetLength(1);
    T[][] jagged = new T[height][];

    for (int i = 0; i < height; i++)
    {
        T[] row = new T[width];
        for (int j = 0; j < width; j++)
        {
            row[j] = array[i, j];
        }
        jagged[i] = row;
    }
    return jagged;
}
static T[,] ToRectangular<T>(this T[][] array)
{
    int height = array.Length, width = array[0].Length;
    T[,] rect = new T[height, width];
    for (int i = 0; i < height; i++)
    {
        T[] row = array[i];
        for (int j = 0; j < width; j++)
        {
            rect[i, j] = row[j];
        }
    }
    return rect;
}
// fill an existing rectangular array from a jagged array
static void WriteRows<T>(this T[,] array, params T[][] rows)
{
    for (int i = 0; i < rows.Length; i++)
    {
        T[] row = rows[i];
        for (int j = 0; j < row.Length; j++)
        {
            array[i, j] = row[j];
        }
    }
}
41
Marc Gravell

Here はInformItのJim Mischelのアーカイブ記事で、長方形とギザギザの両方の多次元配列のソートを処理します。

6
Doug L.

このコードはあなたが望んでいることをする必要があります、私はn×nでそれを一般化していませんが、それは簡単です。とはいえ、MusiGenesisに同意します。これにはもう少し適した別のオブジェクトを使用します(特に、何らかのバインドを行う場合)

(コードを見つけました ここ

string[][] array = new string[3][];

array[0] = new string[3] { "Apple", "Apple", "Apple" };
array[1] = new string[3] { "banana", "banana", "dog" };
array[2] = new string[3] { "cat", "hippo", "cat" };         

for (int i = 0; i < 3; i++)
{
   Console.WriteLine(String.Format("{0} {1} {2}", array[i][0], array[i][1], array[i][2]));
}

int j = 2;

Array.Sort(array, delegate(object[] x, object[] y)
  {
    return (x[j] as IComparable).CompareTo(y[ j ]);
  }
);

for (int i = 0; i < 3; i++)
{
  Console.WriteLine(String.Format("{0} {1} {2}", array[i][0], array[i][1], array[i][2]));
}
1
David Hall
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int[,] arr = { { 20, 9, 11 }, { 30, 5, 6 } };
            Console.WriteLine("before");
            for (int i = 0; i < arr.GetLength(0); i++)
            {
                for (int j = 0; j < arr.GetLength(1); j++)
                {
                    Console.Write("{0,3}", arr[i, j]);
                }
                Console.WriteLine();
            }
            Console.WriteLine("After");

            for (int i = 0; i < arr.GetLength(0); i++) // Array Sorting
            {
                for (int j = arr.GetLength(1) - 1; j > 0; j--)
                {

                    for (int k = 0; k < j; k++)
                    {
                        if (arr[i, k] > arr[i, k + 1])
                        {
                            int temp = arr[i, k];
                            arr[i, k] = arr[i, k + 1];
                            arr[i, k + 1] = temp;
                        }
                    }
                }
                Console.WriteLine();
            }

            for (int i = 0; i < arr.GetLength(0); i++)
            {
                for (int j = 0; j < arr.GetLength(1); j++)
                {
                    Console.Write("{0,3}", arr[i, j]);
                }
                Console.WriteLine();
            }
        }
    }
}
1
Gregory Massov

Array.Sortメソッドをすべて見ることができます http://msdn.Microsoft.com/en-us/library/aa311213(v = vs.71).aspx

例えばArray.Sort(array、delegate(object [] x、object [] y){return(x [i] as IComparable).CompareTo(y [i]);});

from http://channel9.msdn.com/forums/Coffeehouse/189171-Sorting-Two-Dimensional-Arrays-in-C/

1
No_Nick777

したがって、あなたの配列はこのように構成されています(私のC#-fuが弱いので、擬似コードで話をしますが、私が言っていることの要点を取得してください)

string values[rows][columns]

そう value[1][3]は、行1、列3の値です。

列でソートしたいので、問題は配列が90度ずれていることです。

最初のカットとして、回転させるだけでいいですか?

std::string values_by_column[columns][rows];

for (int i = 0; i < rows; i++)
  for (int j = 0; j < columns; j++)
    values_by_column[column][row] = values[row][column]

sort_array(values_by_column[column])

for (int i = 0; i < rows; i++)
  for (int j = 0; j < columns; j++)
    values[row][column] = values_by_column[column][row]

一度に1つの列のみをソートすることがわかっている場合は、ソートするデータを抽出するだけで、これを最適化できます。

  string values_to_sort[rows]
  for (int i = 0; i < rows; i++)
    values_to_sort[i] = values[i][column_to_sort]

  sort_array(values_to_sort)

  for (int i = 0; i < rows; i++)
    values[i][column_to_sort] = values_to_sort[i]

C++では、2次元配列を1次元配列として扱うことができるため、配列へのオフセットを計算する方法でトリックを行うことができますが、C#でそれを行う方法はわかりません。

0
Moishe Lettvin

これを試してください。基本的な戦略は、特定の列を個別にソートし、エントリの元の行を記憶することです。コードの残りは、ソートされた列データを循環し、配列内の行を交換します。トリッキーな部分は、スワップ部分が元の列を効果的に変更するため、元の列を更新することを記憶しています。


        public class Pair<T> {
            public int Index;
            public T Value;
            public Pair(int i, T v) {
                Index = i;
                Value = v;
            }
        }
        static IEnumerable<Pair<T>> Iterate<T>(this IEnumerable<T> source) {
            int index = 0;
            foreach ( var cur in source) {
                yield return new Pair<T>(index,cur);
                index++;
            }
        }
        static void Sort2d(string[][] source, IComparer comp, int col) {
            var colValues = source.Iterate()
                .Select(x => new Pair<string>(x.Index,source[x.Index][col])).ToList();
            colValues.Sort((l,r) => comp.Compare(l.Value, r.Value));
            var temp = new string[source[0].Length];
            var rest = colValues.Iterate();
            while ( rest.Any() ) {
                var pair = rest.First();
                var cur = pair.Value;
                var i = pair.Index;
                if (i == cur.Index ) {
                    rest = rest.Skip(1);
                    continue;
                }

                Array.Copy(source[i], temp, temp.Length);
                Array.Copy(source[cur.Index], source[i], temp.Length);
                Array.Copy(temp, source[cur.Index], temp.Length);
                rest = rest.Skip(1);
                rest.Where(x => x.Value.Index == i).First().Value.Index = cur.Index;
            }
        }

        public static void Test1() {
            var source = new string[][] 
            {
                new string[]{ "foo", "bar", "4" },
                new string[] { "jack", "dog", "1" },
                new string[]{ "boy", "ball", "2" },
                new string[]{ "yellow", "green", "3" }
            };
            Sort2d(source, StringComparer.Ordinal, 2);
        }
0
JaredPar

私はその遅れを知っていますが、ここにあなたが考慮したいかもしれない私の考えがあります。

たとえば、これは配列です

{
m,m,m
a,a,a
b,b,b
j,j,j
k,l,m
}

そして、列2で変換したい場合は、

string[] newArr = new string[arr.length]
for(int a=0;a<arr.length;a++)
newArr[a] = arr[a][1] + a;
// create new array that contains index number at the end and also the coloumn values
Array.Sort(newArr);
for(int a=0;a<newArr.length;a++)
{
int index = Convert.ToInt32(newArr[a][newArr[a].Length -1]);
//swap whole row with tow at current index
if(index != a)
{
string[] arr2 = arr[a];
arr[a] = arr[index];
arr[index] = arr2;
}
}

おめでとうございます。希望の列で配列をソートしました。これを編集して、他のデータ型で動作するようにできます

0
Mamoon Ahmed

データを読み込んだり取得したりするときに、データを汎用タプルとして取得できれば、はるかに簡単になります。次に、タプルの目的の列を比較するソート関数を記述する必要があり、タプルの単一次元配列があります。

0
Raindog

これは古い質問ですが、ここでは InformItのJim Mischelの記事Doug L。 によってリンクされたクラスに基づいて作成しました。

_class Array2DSort : IComparer<int>
{
    // maintain a reference to the 2-dimensional array being sorted
    string[,] _sortArray;
    int[] _tagArray;
    int _sortIndex;

    protected string[,] SortArray { get { return _sortArray; } }

    // constructor initializes the sortArray reference
    public Array2DSort(string[,] theArray, int sortIndex)
    {
        _sortArray = theArray;
        _tagArray = new int[_sortArray.GetLength(0)];
        for (int i = 0; i < _sortArray.GetLength(0); ++i) _tagArray[i] = i;
        _sortIndex = sortIndex;
    }

    public string[,] ToSortedArray()
    {
        Array.Sort(_tagArray, this);
        string[,] result = new string[
            _sortArray.GetLength(0), _sortArray.GetLength(1)];
        for (int i = 0; i < _sortArray.GetLength(0); i++)
        {
            for (int j = 0; j < _sortArray.GetLength(1); j++)
            {
                result[i, j] = _sortArray[_tagArray[i], j];
            }
        }
        return result;
    }

    // x and y are integer row numbers into the sortArray
    public virtual int Compare(int x, int y)
    {
        if (_sortIndex < 0) return 0;
        return CompareStrings(x, y, _sortIndex);
    }

    protected int CompareStrings(int x, int y, int col)
    {
        return _sortArray[x, col].CompareTo(_sortArray[y, col]);
    }
}
_

列5でソートしたい任意のサイズの未ソートの2D配列dataが与えられた場合、これを行うだけです:

_        Array2DSort comparer = new Array2DSort(data, 5);
        string[,] sortedData = comparer.ToSortedArray();
_

仮想Compareメソッドと保護されたSortArrayに注意してください。これにより、特定の列で常にソートする、または複数の列または目的に応じて特別なソートを行う特殊なサブクラスを作成できます。 CompareStringsが分割され保護されている理由でもあります。すべてのサブクラスは、完全なSortArray[x, col].CompareTo(SortArray[y, col])構文を入力する代わりに、単純な比較に使用できます。

0
Chuck Wilbur

上記のMusiGenesisによって提案されたDataTableアプローチが好きです。それについての素晴らしいことは、列名を使用する任意の有効なSQL「order by」文字列でソートできることです。 「x、y desc、zによる順序付け」の場合は「x、y desc、z」。 (FWIW、列順序を使用して動作させることができませんでした。例えば、「3,2,1」の「3,2,1」)整数のみを使用しましたが、DataTableに混合型データを追加し、どの方法で並べ替えます。

以下の例では、まず、未ソートの整数データをSandboxのtblToBeSortedにロードしました(表示されていません)。テーブルとそのデータが既に存在しているので、2D整数配列にデータを(ソートなしで)ロードし、次にDataTableにロードします。 DataRowsの配列は、DataTableのソートされたバージョンです。この例は、DBから配列を読み込んで並べ替えることができるという点で少し奇妙ですが、DataTableオブジェクトで使用するために並べ替えられていない配列をC#に取得したかっただけです。

static void Main(string[] args)
{
    SqlConnection cnnX = new SqlConnection("Data Source=r90jroughgarden\\;Initial Catalog=Sandbox;Integrated Security=True");
    SqlCommand cmdX = new SqlCommand("select * from tblToBeSorted", cnnX);
    cmdX.CommandType = CommandType.Text;
    SqlDataReader rdrX = null;
    if (cnnX.State == ConnectionState.Closed) cnnX.Open();

    int[,] aintSortingArray = new int[100, 4];     //i, elementid, planid, timeid

    try
    {
        //Load unsorted table data from DB to array
        rdrX = cmdX.ExecuteReader();
        if (!rdrX.HasRows) return;

        int i = -1;
        while (rdrX.Read() && i < 100)
        {
            i++;
            aintSortingArray[i, 0] = rdrX.GetInt32(0);
            aintSortingArray[i, 1] = rdrX.GetInt32(1);
            aintSortingArray[i, 2] = rdrX.GetInt32(2);
            aintSortingArray[i, 3] = rdrX.GetInt32(3);
        }
        rdrX.Close();

        DataTable dtblX = new DataTable();
        dtblX.Columns.Add("ChangeID");
        dtblX.Columns.Add("ElementID");
        dtblX.Columns.Add("PlanID");
        dtblX.Columns.Add("TimeID");
        for (int j = 0; j < i; j++)
        {
            DataRow drowX = dtblX.NewRow();
            for (int k = 0; k < 4; k++)
            {
                drowX[k] = aintSortingArray[j, k];
            }
            dtblX.Rows.Add(drowX);
        }

        DataRow[] adrowX = dtblX.Select("", "ElementID, PlanID, TimeID");
        adrowX = dtblX.Select("", "ElementID desc, PlanID asc, TimeID desc");

    }
    catch (Exception ex)
    {
        string strErrMsg = ex.Message;
    }
    finally
    {
        if (cnnX.State == ConnectionState.Open) cnnX.Close();
    }
}
0