2017-06-28 212 views
2

我想對包含人的邏輯組和人的分數的數組進行排序。根據組內的值對組進行排序

Name | Group | Score 
---------------------- 
Alfred |  1 |  3 
Boris |  3 |  3 
Cameron|  3 |  1 
Donna |  1 |  2 
Emily |  2 |  2 

人們應根據組中的最低分數按組進行排序。因此,第3組是第一個,因爲它包含得分最低的人。然後是第1組中的人,因爲它具有次低得分的人(並且組數小於組2)。

那麼結果將是:卡梅隆,鮑里斯,唐娜,阿爾弗雷德,艾米麗

我已經做到了這一點,但我想知道是否有這樣做的更好的方法。我收到一個數組,並最終按照正確的順序對數組進行排序。

我使用LINQ(主要從Linq order by, group by and order by each group?獲得)創建一個目標排序數組,映射一個人應該在哪裏,與他們當前在數組中的位置進行比較。

然後我使用Array.Sort使用我的目標排序數組,但LINQ語句創建的數組在索引和值方面是「相反的」,所以我必須顛倒索引和值(而不是順序)。

我附上我的代碼如下。有沒有更好的方法來做到這一點?

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace Sorter 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      // Sample person array. 
      // Lower score is better. 
      Person[] peopleArray = new Person[] 
      { 
       new Person { Name = "Alfred", Group = "1", Score = 3, ArrayIndex = 0 }, 
       new Person { Name = "Boris", Group = "3", Score = 3, ArrayIndex = 1 }, 
       new Person { Name = "Cameron", Group = "3", Score = 1, ArrayIndex = 2 }, 
       new Person { Name = "Donna", Group = "1", Score = 2, ArrayIndex = 3 }, 
       new Person { Name = "Emily", Group = "2", Score = 2, ArrayIndex = 4 } 
      }; 

      // Create people list. 
      List<Person> peopleModel = peopleArray.ToList(); 

      // Sort the people based on the following: 
      // Sort people into groups (1, 2, 3) 
      // Sort the groups by the lowest score within the group. 
      // So, the first group would be group 3, because it has the 
      // member with the lowest score (Cameron with 1). 
      // The people are therefore sorted in the following order: 
      // Cameron, Boris, Donna, Alfred, Emily 
      int[] targetOrder = peopleModel.GroupBy(x => x.Group) 
              .Select(group => new 
              { 
               Rank = group.OrderBy(g => g.Score) 
              }) 
              .OrderBy(g => g.Rank.First().Score) 
              .SelectMany(g => g.Rank) 
              .Select(i => i.ArrayIndex) 
              .ToArray(); 

      // This will give the following array: 
      // [2, 1, 3, 0, 4] 
      // I.e: Post-sort, 
      // the person who should be in index 0, is currently at index 2 (Cameron). 
      // the person who should be in index 1, is currently at index 1 (Boris). 
      //     etc. 

      // I want to use my target array to sort my people array. 

      // However, the Array.sort method works in the reverse. 
      // For example, in my target order array: [2, 1, 3, 0, 4] 
      // person currently at index 2 should be sorted into index 0. 
      // I need the following target order array: [3, 1, 0, 2, 4], 
      // person currently at index 0, should be sorted into index 3 
      // So, "reverse" the target order array. 
      int[] reversedArray = ReverseArrayIndexValue(targetOrder); 

      // Finally, sort the base array. 
      Array.Sort(reversedArray, peopleArray); 

      // Display names in order. 
      foreach (var item in peopleArray) 
      { 
       Console.WriteLine(item.Name); 
      } 

      Console.Read(); 
     } 

     /// <summary> 
     /// "Reverses" the indices and values of an array. 
     /// E.g.: [2, 0, 1] becomes [1, 2, 0]. 
     /// The value at index 0 is 2, so the value at index 2 is 0. 
     /// The value at index 1 is 0, so the value at index 0 is 1. 
     /// The value at index 2 is 1, so the value at index 1 is 2. 
     /// </summary> 
     /// <param name="target"></param> 
     /// <returns></returns> 
     private static int[] ReverseArrayIndexValue(int[] target) 
     { 
      int[] swappedArray = new int[target.Length]; 

      for (int i = 0; i < target.Length; i++) 
      { 
       swappedArray[i] = Array.FindIndex(target, t => t == i); 
      } 

      return swappedArray; 
     } 
    } 
} 
+0

什麼是你想要的最終結果?你只是想要一個'List '按你的標準排序嗎?我有點不清楚你正在拋出的所有數組是什麼,只是你嘗試工作的一部分,以及需要輸出什麼...... – Chris

+0

組1和組2都有第二低的分數。將使用什麼標準來確定第一組是下一個? –

+0

@Chris我希望的最終結果是我在這種情況下以排序順序(peopleArray)開始的數組。 – SortingAndOrdering

回答

2

據我瞭解,你要排序的輸入數組就位。

首先,排序部分可以簡化(和更有效率)由第一OrderBy得分和然後GroupBy組,利用定義的Enumerable.GroupBy行爲:

的IGrouping < TKEY的,TElement>對象產生按照產生每個IGrouping < TKey,TElement>的第一個關鍵字的元素的順序排列。分組中的元素按它們在源代碼中出現的順序排序。

一旦你的,你需要的是扁平化的結果,重複它(這樣執行的話),並把產生的項目在新的地方:

var sorted = peopleArray 
    .OrderBy(e => e.Score) 
    .ThenBy(e => e.Group) // to meet your second requirement for equal Scores 
    .GroupBy(e => e.Group) 
    .SelectMany(g => g); 
int index = 0; 
foreach (var item in sorted) 
    peopleArray[index++] = item; 
+1

這很好。謝謝。天哪..它比我原來的實現少得多。 – SortingAndOrdering

+1

不客氣,這就是爲什麼我們在這裏:) –

2

不知道我是否真正理解所希望的結果應該是什麼,但是這至少給的例子在評論中提到相同的順序:

var sortedNames = peopleArray 
      // group by group property 
      .GroupBy(x => x.Group) 
      // order groups by min score within the group 
      .OrderBy(x => x.Min(y => y.Score)) 
      // order by score within the group, then flatten the list 
      .SelectMany(x => x.OrderBy(y => y.Score)) 
      // doing this only to show that it is in right order 
      .Select(x => 
      { 
       Console.WriteLine(x.Name); 
       return false; 
      }).ToList(); 
+0

幹得好。我唯一的評論將是在你的測試選擇你應該'返回x'而不是'返回false'。否則,你會在最後得到一個布爾列表。雖然我知道在最後選擇的實時代碼中不會存在,但它仍然更適合演示儘可能完整的功能。 – Chris

+0

在這個LINQ排序工作完美,但有沒有辦法將這種排序應用到原始數組?我不想結束它的一個排序副本,我需要原始的一個排序。 – SortingAndOrdering

0

如果你期望的結果是少代碼行。這個怎麼樣?

var peoples = peopleModel.OrderBy(i => i.Score).GroupBy(g => 
       g.Group).SelectMany(i => i, (i, j) => new { j.Name }); 

1)列出的分數

2)集團也通過分組

3)拼合分組列表,並使用創建 「名稱」 屬性的新名單的SelectMany

的信息使用匿名類型 https://dzone.com/articles/selectmany-probably-the-most-p

0
int[] order = Enumerable.Range(0, peopleArray.Length) 
         .OrderBy(i => peopleArray[i].Score) 
         .GroupBy(i => peopleArray[i].Group) 
         .SelectMany(g => g).ToArray();   // { 2, 1, 3, 0, 4 } 

Array.Sort(order, peopleArray); 

Debug.Print(string.Join(", ", peopleArray.Select(p => p.ArrayIndex))); // "3, 1, 0, 2, 4" 
相關問題