我正在尝试对包含逻辑人群和人民分数的数组进行排序。
Name | Group | Score
----------------------
Alfred | 1 | 3
Boris | 3 | 3
Cameron| 3 | 1
Donna | 1 | 2
Emily | 2 | 2
应根据组中的最低分数按组分组人员。因此,第3组是第一个,因为它包含得分最低的人。然后是第1组中的人,因为它具有次要得分最低的人(并且组编号低于组2)。
结果将是:Cameron,Boris,Donna,Alfred,Emily
我已经完成了这项工作,但我想知道是否有更好的方法。我收到一个数组,最后按正确的顺序对数组进行排序。
我使用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 :(得分: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();
答案 1 :(得分:2)
据我了解,您希望对输入数组进行排序。
首先,可以使用Enumerable.GroupBy
的定义行为,通过首先OrderBy
分数然后GroupBy
组简化(并提高效率)排序部分:
IGrouping&lt; TKey,TElement&gt;根据源中元素的顺序产生对象,这些元素产生每个IGrouping&lt; TKey,TElement&gt;的第一个键。分组中的元素按它们在源中出现的顺序产生。
一旦你拥有了它,你所需要的只是将结果展平,迭代它(从而执行它)并将得到的项目放在新的位置:
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;
答案 2 :(得分:0)
如果您想要的结果是较少的代码行。怎么样?
var peoples = peopleModel.OrderBy(i => i.Score).GroupBy(g =>
g.Group).SelectMany(i => i, (i, j) => new { j.Name });
1)按分数列出的订单
2)通过分组
对其进行分组3)展平分组列表并使用&#34; Name&#34;创建新列表。使用SelectMany的属性
有关使用匿名类型的信息 https://dzone.com/articles/selectmany-probably-the-most-p
答案 3 :(得分: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"