2017-06-28 213 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" 
相关问题