2008-10-24 115 views
30

Tôi có một mảng hai chiều (của Strings) tạo nên bảng dữ liệu của tôi (của các hàng và cột). Tôi muốn sắp xếp mảng này theo bất kỳ cột nào. Tôi đã cố gắng để tìm một thuật toán để làm điều này trong C#, nhưng đã không thành công.Làm cách nào để sắp xếp mảng hai chiều trong C#?

Mọi trợ giúp đều được đánh giá cao.

Trả lời

20

Tải mảng chuỗi hai chiều của bạn vào một DataTable thực tế (System.Data.DataTable), và sau đó sử dụng phương thức Select() của đối tượng DataTable để tạo mảng sắp xếp các đối tượng DataRow (hoặc sử dụng một DataView cho một hiệu ứng tương tự)).

// 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"); 

Bạn cũng có thể viết phương pháp của riêng mình để sắp xếp mảng hai chiều. Cả hai cách tiếp cận sẽ là những trải nghiệm học tập hữu ích, nhưng cách tiếp cận DataTable sẽ giúp bạn bắt đầu học cách xử lý dữ liệu bảng trong ứng dụng C# tốt hơn.

+0

Điều đó nghe có vẻ thú vị, bạn có thể đăng hoặc liên kết đến một số ví dụ mã hay không. – Jack

+0

Xong. Nó có thể có một lỗi ở đâu đó - tôi đã viết nó trong notepad. – MusiGenesis

+0

Ngạc nhiên bạn đã viết rằng trong notepad - ở mức nào, nó hoạt động rất tốt. Cảm ơn bạn. – Jack

6

Here là một bài viết được lưu trữ từ Jim Mischel tại InformIt xử lý việc sắp xếp cho cả mảng đa chiều và hình chữ nhật lởm chởm.

+0

Ví dụ đó không thực sự sắp xếp mảng; LINQ sẽ tạo ra một chuỗi được sắp xếp, nhưng chỉ khi bạn nắm bắt được kết quả ... nó không sắp xếp mảng hiện có. Điều này có thể chỉ là: string [] names = {"Smith", "Snyder", "Baker", "Jonson", "Ballmer"}; Array.Sort (tên); –

+0

Tôi có thể thấy những gì bạn đang nói - Tôi sẽ xóa ví dụ sai sót, nhưng để lại trong liên kết đến bài viết sắp xếp. PS - cảm ơn vì đã cho tôi biết lý do bỏ phiếu xuống. Bạn không thấy điều đó thường xuyên nhưng nó thực sự là xây dựng! –

+1

Và tôi đã xóa bỏ phiếu bầu vì bạn đã sửa nó ;-p –

0

Vì vậy, mảng của bạn được cấu trúc như thế này (tôi sẽ nói chuyện trong giả vì tôi C# -fu là yếu, nhưng tôi hy vọng bạn sẽ có được các ý chính về những gì tôi đang nói)

string values[rows][columns] 

Vì vậy value[1][3] là giá trị tại hàng 1, cột 3.

Bạn muốn sắp xếp theo cột, do đó, vấn đề là mảng của bạn bị tắt 90 độ.

Là lần cắt đầu tiên, bạn có thể xoay nó không?

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] 

Nếu bạn biết bạn chỉ muốn sắp xếp một cột cùng một lúc, bạn có thể tối ưu hóa này rất nhiều bằng cách chỉ cần giải nén các dữ liệu bạn muốn sắp xếp:

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] 

Trong C++ bạn có thể chơi thủ đoạn với cách tính toán offset vào mảng (vì bạn có thể xử lý mảng hai chiều của mình dưới dạng mảng một chiều) nhưng tôi không chắc chắn cách thực hiện điều đó trong C#.

1

Mã này nên làm những gì bạn đang làm sau, tôi đã không khái quát nó cho n bởi n, nhưng đó là thẳng về phía trước. Điều đó nói rằng - Tôi đồng ý với MusiGenesis, sử dụng một đối tượng đó là một chút tốt hơn phù hợp với này (đặc biệt là nếu bạn có ý định làm bất kỳ loại ràng buộc)

(Tôi tìm thấy mã here)

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])); 
} 
35

Can Tôi kiểm tra - bạn có nghĩa là một mảng hình chữ nhật ([,]) hoặc một mảng răng cưa ([][])?

Việc sắp xếp một mảng có răng cưa khá dễ dàng; Tôi có một cuộc thảo luận về điều đó here. Rõ ràng trong trường hợp này, Comparison<T> sẽ liên quan đến một cột thay vì sắp xếp theo thứ tự - nhưng rất giống nhau.

Sắp xếp một mảng hình chữ nhật là phức tạp hơn ... Tôi có thể bị cám dỗ sao chép dữ liệu vào một mảng hình chữ nhật hoặc List<T[]> và sắp xếp tại đó, sau đó sao chép lại.

Dưới đây là một ví dụ sử dụng một mảng lởm chởm:

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])); 
} 

Để làm việc với một mảng hình chữ nhật ... tốt, đây là một số mã để trao đổi giữa hai một cách nhanh chóng ...

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]; 
     } 
    } 
} 
+0

Hình chữ nhật. – Jack

+1

OK; Tôi đã thêm một số thủ thuật ;-p –

+0

Điều này thực sự đẹp hơn .... –

0

Hãy dùng thử. Chiến lược cơ bản là sắp xếp cột cụ thể một cách độc lập và ghi nhớ hàng gốc của mục nhập. Phần còn lại của mã sẽ chu kỳ thông qua dữ liệu cột được sắp xếp và hoán đổi các hàng trong mảng. Phần khó khăn là nhớ để cập nhật các cột ban đầu như phần trao đổi sẽ có hiệu quả thay đổi cột ban đầu.


     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

Nếu bạn có thể lấy dữ liệu làm bộ dữ liệu chung khi bạn đọc hoặc lấy dữ liệu, nó sẽ dễ dàng hơn nhiều; sau đó bạn sẽ phải viết một hàm sắp xếp so sánh cột mong muốn của bộ tuple và bạn có một mảng thứ nguyên duy nhất của bộ dữ liệu.

0

Tôi thích phương pháp tiếp cận DataTable được đề xuất bởi MusiGenesis ở trên. Điều tuyệt vời về nó là bạn có thể sắp xếp theo bất kỳ thứ tự SQL hợp lệ nào bằng 'chuỗi sử dụng tên cột, ví dụ: "x, y desc, z" cho 'order by x, y desc, z'. (FWIW, tôi không thể làm cho nó hoạt động bằng cách sử dụng các cột thứ tự, ví dụ "3,2,1" cho 'thứ tự bằng 3,2,1') Tôi chỉ sử dụng các số nguyên, nhưng rõ ràng bạn có thể thêm dữ liệu kiểu hỗn hợp vào DataTable và sắp xếp nó theo bất kỳ cách nào.

Trong ví dụ bên dưới, trước tiên tôi đã tải một số dữ liệu số nguyên chưa phân loại vào một tblToBeSorted trong Sandbox (không được hiển thị). Với bảng và dữ liệu của nó đã tồn tại, tôi tải nó (chưa phân loại) vào một mảng số nguyên 2D, sau đó đến một DataTable. Mảng DataRows là phiên bản được sắp xếp của DataTable. Ví dụ là một chút lẻ trong đó tôi tải mảng của tôi từ DB và có thể đã sắp xếp nó sau đó, nhưng tôi chỉ muốn có được một mảng chưa được phân loại vào C# để sử dụng với đối tượng DataTable.

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(); 
    } 
} 
1
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(); 
      } 
     } 
    } 
} 
0

Đây là một câu hỏi cũ, nhưng đây là một lớp học tôi chỉ được xây dựng dựa trên the article from Jim Mischel at InformIt liên kết bởi 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]); 
    } 
} 

Với một 2D mảng không được phân loại data kích thước tùy ý mà bạn muốn sắp xếp trên cột 5 bạn chỉ cần làm điều này:

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

Lưu ý virtualPhương phápvà được bảo vệ SortArray để bạn có thể tạo các lớp con chuyên biệt luôn phân loại trên một cột cụ thể hoặc phân loại chuyên biệt trên nhiều cột hoặc bất kỳ điều gì bạn muốn làm. Đó cũng là lý do tại sao CompareStrings được chia nhỏ và được bảo vệ - bất kỳ lớp con nào cũng có thể sử dụng nó để so sánh đơn giản thay vì nhập cú pháp đầy đủ SortArray[x, col].CompareTo(SortArray[y, col]).

0

Tôi biết nó muộn nhưng đây là suy nghĩ của tôi bạn có thể muốn xem xét.

ví dụ này là mảng

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

và bạn muốn chuyển đổi nó bằng số coloumn 2, sau đó

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; 
} 
} 

Xin chúc mừng bạn đã sắp xếp mảng bởi coloumn mong muốn. Bạn có thể chỉnh sửa điều này để làm việc với các loại dữ liệu khác

Các vấn đề liên quan