장난감 연구소

[C#] Array.Sort(), Linq로 2차원 배열 정렬하기 본문

프로그래밍/C#

[C#] Array.Sort(), Linq로 2차원 배열 정렬하기

changi1122 2019. 5. 10. 12:38

    Array.Sort 함수(메서드)를 사용해서 2차원 배열 정렬하는 방법을 찾아 헤맸으나 단순 2차원 배열에서 한 열을 기준으로 정렬하는 것은 어려운 것 같다. 그래서 이런저런 방법을 찾아보고 시도하게 되었다. 그리고 잊어버릴까해서 대안으로 사용할 수 있는 방법을 정리하였다. 더 나은 방법을 알고 있다면, 꼭 알려주기 바란다.

    추천 : 가변배열 사용하기 (Linq)

    C#에서 2차원 배열을 정의하는 방법은 두 가지가 있다. 원래 2차원 배열을 int[,] arr = new int[n][m]과 같이 선언하였다면, 가변배열은 아래와 같이 선언한다.

    int[][] arr = new int[3][];
    arr[0] = new int[] { 1, 2, 3 }; //Console.Write(arr[0][0]); : 1 출력
    arr[1] = new int[] { 6, 5, 4 };
    arr[2] = new int[] { 8, 7 };

    가변배열은 배열의 배열이다. 위 코드에서 arr는 arr[0], arr[1]...을 원소로 가지고, arr[0]은 1차원 정수 배열이다. C언어와 문법이 비슷하지만, 그와 다르게 C#의 가변배열은 arr[2]처럼 각 1차원 배열의 크기가 달라도 된다.

    가변 배열을 사용하는 이유는 System.Linq에 정의된 OrderBy 함수를 이용하기 위해서이다. 예시로 확인하자.

    using System.Linq;
    
    int[][] arr = new int[3][]; //위의 가변배열
    
    //0번, 1번 열을 기준으로 정렬한다.
    IOrderedEnumerable<int[]> sortedByFirst = arr.OrderBy(y => y[0]);
    IOrderedEnumerable<int[]> sortedBySecond = arr.OrderBy(y => y[1]);
    //sortedByFirst.ElementAt(0) : { 1, 2, 3 }
    
    //ToArray()로 가변배열로 만들 수 있다.
    int[][] sortedArr = sortedByFirst.ToArray();
    
    //ThenBy()로 앞의 열이 동일할 때 사용될 두 번째, 세 번째 열을 조건으로 줄 수도 있다.
    var subRuleSorted = x.OrderBy(y => y[0]).ThenBy(y => y[1]).ThenBy(y => y[2]);

    가변배열 arr가 있을 때 arr.OrderBy(y => y[0])으로 첫 번째 열을 조건으로 정렬할 수 있다. 이때 출력 형식이 IOrderedEnumerable<int[]>임에 유의하자. 그대로 적을 수도 있고, var를 사용할 수도 있다. sortedByFirst.ElementAt(i)와 같이 정렬된 배열을 사용할 수 있고, ToArray()로 다시 가변배열로 만들 수도 있다.

    뒤에 ThenBy()를 붙여주기만 하면 첫 번째 열이 동일할 때 사용될 분류 조건을 붙일 수도 있다. 2차원 배열과 비슷하게 사용하면서, 간단한 코드로 구현 가능하기에 이 방법을 추천한다.

    #정보 출처

    2차원 배열을 1차원 배열 두 개로 나누기 (Array.Sort())

    int[] keyArr = new int[N];
    int[] valueArr = new int[N];
    
    Array.Sort(keyArr, valueArr);

    만약에 2차원 배열의 열이 두 개였다면, 정렬의 조건이 되는 keyArr와 valueArr라는 두 1차원 배열로 나눈 후 Array.Sort() 함수를 사용할 수 있다. 위 코드와 같이 사용시 Array.Sort()는 keyArr를 조건으로 keyArr와 valueArr를 동시에 정렬해준다.

    정렬이 필요할 때 2차원 배열을 1차원 배열 두 개로 나누는 방식으로도 응용 가능할 것 같다.

    #Reference

    배열을 대신할, 클래스 만들기 (Array.Sort())

    //Array.Sort 사용을 위해 IComparable을 상속받아야 한다.
    class Data : IComparable
    {
      public int data1;
      public int data2;
    
      public Data(int data1, int data2) //생성자
      {
        this.data1 = data1;
        this.data2 = data2;
      }
    
      //IComparable 상속 시 CompareTo 메서드를 정의해야 한다.
      //this와 obj를 비교했을 때, this의 값이 작으면 -1을 반환하고, 크면 1을 반환한다.
      //Sort()에서 CompareTo()의 반환값을 기준으로 정렬하는데 이를 다양하게 응용 가능하다.
      //아래 코드는 data1을 첫번째 조건으로 정렬하고, data1이 같으면 data2로 정렬하는 코드이다.
      public int CompareTo(object obj)
      {
        if (data1 < (obj as Data).data1)
          return -1;
        else if (data1 == (obj as Data).data1)
        {
          if (data2 < (obj as Data).data2)
            return -1;
          else
            return 1;
        }
        else 
          return 1; 
      }
    }
    
    class Program
    {
      static void Main(string[] args)
      {
        Data[] datas = new Data[10];
        for (int i = 0; i < 10; ++i)
          datas[i] = new Data(data1, data2);
    
        //정렬
        Array.Sort(datas);
        
      }
    }

    다른 방법 하나는 2차원 배열 대신 한 행을 객체로 만들어 객체들의 배열로 만드는 것이다. 객체들은 해당 클래스가 IComparable 인터페이스를 상속한 클래스이면, Array.Sort()를 사용하여 정렬할 수 있다. IComparable 인터페이스에서는 상속한 클래스가 CompareTo 메서드를 정의하도록 하고 있다. CompareTo 메서드를 구현할 때는 this 객체와 obj 인자를 비교할 때 this가 값이 작으면(앞으로 정렬되야 하면) -1을 반환하고, this가 값이 크면(뒤로 정렬되야 하면) 1을 반환하면 된다. Array.Sort()에서는 CompareTo()의 반환값을 기준으로 정렬한다.

    위 코드는 조금 응용하여 CompareTo 메서드를 정의하였는데, data1을 첫번째 기준으로 정렬하고, data1의 값이 같은 때 data2를 기준으로 정렬하도록 하였다. 이와 같이 CompareTo 메서드를 응용하여 다양한 정렬 기준을 사용할 수 있다.

    728x90
    개 댓글