【问题标题】:How do I sort a two-dimensional (rectangular) array in C#?如何在 C# 中对二维(矩形)数组进行排序?
【发布时间】:2010-09-18 22:40:22
【问题描述】:

我有一个二维数组(字符串),它构成了我的数据表(行和列)。我想按任何列对该数组进行排序。我试图在 C# 中找到一种算法,但没有成功。

感谢任何帮助。

【问题讨论】:

    标签: c# arrays sorting


    【解决方案1】:

    我可以检查一下 - 您是指矩形阵列 ([,]) 还是锯齿状阵列 ([][])?

    对锯齿状数组进行排序非常容易;我对此进行了讨论here。显然,在这种情况下,Comparison<T> 将涉及一列而不是按序号排序 - 但非常相似。

    对矩形数组进行排序比较棘手...我可能很想将数据复制到矩形数组或List<T[]>,然后在那里进行排序,然后再复制回来。

    这是一个使用锯齿状数组的示例:

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

    对于使用矩形数组...好吧,这里有一些代码可以在两者之间即时交换...

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

    【讨论】:

    • Sort方法中需要做哪些更改才能对数据进行降序排序。
    • @Homam comparer.Compare(y[col],x[col]))(反向xy 那里)
    【解决方案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");
    

    您还可以编写自己的方法来对二维数组进行排序。这两种方法都是有用的学习经验,但 DataTable 方法可以让您开始学习在 C# 应用程序中处理数据表的更好方法。

    【讨论】:

    • 这听起来很有趣,请您发布或链接到一些代码示例。
    • 完成。它可能在某处有错误 - 我在记事本中写了它。
    • 很惊讶你在记事本中写了这些——无论如何,它工作得很好。谢谢。
    【解决方案3】:
    Array.Sort(array, (a, b) => { return a[0] - b[0]; });
    

    【讨论】:

    • 欢迎来到 SO。请不要发布仅代码的答案。由于代码对您来说很简单,因此其他人可能很难理解它以及您使用这种方法的原因。请详细说明您的代码为什么这样做。另外,请注意这个问题是从 2008 年开始的。
    • @Korashen:你有一个很好的建议。你能告诉我那行代码是否会首先按第一个索引对整个数组进行排序,然后下一步将按数组的第二个索引对整个数组进行排序?这是否需要“使用 System.Linq”?谢谢。
    【解决方案4】:

    Here 是来自 InformIt 的 Jim Mischel 的存档文章,它处理矩形和锯齿状多维数组的排序。

    【讨论】:

    • 该示例实际上并未对数组进行排序; LINQ 将产生一个排序序列,但前提是你捕获结果......它不会对现有数组进行排序。这可能只是: string[] names = {"Smith","Snyder","Baker","Jonson","Ballmer"}; Array.Sort(names);
    • 我明白你在说什么 - 我将删除有缺陷的示例,但保留排序文章的链接。 PS - 谢谢你告诉我投反对票的原因。你不会经常看到它,但它确实是有建设性的!
    • 链接已损坏。它重定向到网站的主页
    • 谢谢 KFL,我编辑了指向存档版本的链接。尽管我更愿意为他们链接到 InformIT 站点上的当前版本,但我无法轻易找到该内容。它似乎已被删除。
    【解决方案5】:
    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();
                }
            }
        }
    }
    

    【讨论】:

      【解决方案6】:

      这段代码应该做你想要的,我没有将它概括为 n 乘 n,但这是直截了当的。也就是说 - 我同意 MusiGenesis,使用另一个更适合此的对象(特别是如果您打算进行任何类型的绑定)

      (我找到了代码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]));
      }
      

      【讨论】:

        【解决方案7】:

        也可以看看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 ]);});

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

        【讨论】:

        • 嗨,No_Nick,如果我有一个二维数组,我想同时对这个数组进行 2 次排序: (1) 首先,按第一个索引排序; (2)接下来对于第一个索引值相同的所有元素,请按第二个索引排序; - 你能告诉我如何用你写的 Array.Sort() 来做到这一点吗?谢谢。
        【解决方案8】:

        所以你的数组的结构是这样的(我会用伪代码说话,因为我的 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]
        

        如果您知道一次只想对一列进行排序,则可以通过提取要排序的数据来优化这一点:

          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++ 中,您可以玩一些技巧来计算数组的偏移量(因为您可以将二维数组视为一维数组),但我不确定如何在 c# 中执行此操作。

        【讨论】:

          【解决方案9】:

          试试这个。基本策略是对特定列独立排序并记住条目的原始行。其余代码将循环遍历已排序的列数据并换出数组中的行。棘手的部分是要记住更新原始列,因为交换部分将有效地改变原始列。

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

          【讨论】:

            【解决方案10】:

            如果在读入或检索数据时可以将数据作为通用元组获取,那会容易得多;那么你只需要编写一个比较所需的元组列的排序函数,并且你有一个单维元组数组。

            【讨论】:

              【解决方案11】:

              这是一个老问题,但这是我刚刚基于the article from Jim Mischel at InformIt 构建的一个类,由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]);
                  }
              }
              

              给定一个任意大小的未排序二维数组data,您希望在第 5 列上进行排序,您只需执行以下操作:

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

              注意虚拟Compare 方法和受保护的SortArray,这样您就可以创建专门的子类,这些子类始终对特定列进行排序,或者对多列进行专门的排序或任何您想做的事情。这也是 CompareStrings 被拆分和保护的原因 - 任何子类都可以使用它进行简单的比较,而不是输入完整的 SortArray[x, col].CompareTo(SortArray[y, col]) 语法。

              【讨论】:

                【解决方案12】:

                我喜欢上面MusiGenesis 提出的DataTable 方法。它的好处是您可以按任何使用列名的有效 SQL 'order by' 字符串进行排序,例如"x, y desc, z" 表示“按 x, y desc, z 排序”。 (FWIW,我无法使用列序号使其工作,例如“3,2,1”表示“按 3,2,1 排序”)我只使用整数,但显然您可以将混合类型数据添加到 DataTable 和以任何方式排序。

                在下面的示例中,我首先将一些未排序的整数数据加载到 Sandbox 中的 tblToBeSorted 中(未显示)。由于表及其数据已经存在,我将其(未排序)加载到二维整数数组中,然后加载到 DataTable。 DataRows 数组是 DataTable 的排序版本。这个例子有点奇怪,因为我从数据库中加载了我的数组,然后可以对其进行排序,但我只是想将一个未排序的数组放入 C# 中以与 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();
                    }
                }
                

                【讨论】:

                  【解决方案13】:

                  我知道为时已晚,但这是我的想法,您可能想考虑一下。

                  例如这是数组

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

                  恭喜您已按所需列对数组进行了排序。您可以对其进行编辑以使其与其他数据类型一起使用

                  【讨论】:

                    【解决方案14】:

                    假设它是一个交错数组,你可以使用 LINQ 或 Array.Sort() 方法对其进行排序。

                    方法一:使用 LINQ

                    var myOrderedRows = myArray.OrderBy(row => row[columnIndex]).ToArray();
                    

                    在这里,LINQ 创建了一个新的 IEnumerable,需要将其转换为数组(使用 ToArray())myOrderedRows。您的原始数组仍未排序。更多细节可以在文档here中找到。

                    方法二:使用 Array.Sort()

                    Array.Sort(myArray, (p, q) => p[columnIndex].CompareTo(q[columnIndex]));
                    

                    在这种情况下,您的原始数组已就地排序。您还可以提供自定义比较器以获得更多比较规则。更多细节可以在文档here中找到。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2021-11-07
                      • 1970-01-01
                      • 2015-01-31
                      • 2019-05-17
                      • 2015-01-30
                      • 2012-12-15
                      相关资源
                      最近更新 更多