C# LINQ 组按属性集合,然后按列表定义的显式顺序对每个组进行排序[重复]

Posted

技术标签:

【中文标题】C# LINQ 组按属性集合,然后按列表定义的显式顺序对每个组进行排序[重复]【英文标题】:C# LINQ group collection by attribute then sort each group by explicit order defined by a list [duplicate] 【发布时间】:2021-11-15 18:52:23 【问题描述】:

我有一组我想要的 C# 对象

    给定属性的分组 然后按另一个属性对每个组进行排序。 排序顺序由列表明确定义。 然后我只想从每个组中获取最上面的项目

例如,考虑:

data = [
  ​​​​​"name": "Alice", "country": "UK", "age": 30​​​​​,
  ​​​​​​​"name": "Bob", "country": "KE", "age": 20​​​​​​​,
  ​​​​​​​"name": "Charlie", "country": "UK", "age": 30​​​​​​​,
  ​​​​​​​"name": "Alice", "country": "KE", "age": 40​​​​​​​,
  ​​​​​​​"name": "Bob", "country": "AU", "age": 50​​​​​​​,
  ​​​​​​​"name": "David", "country": "USA", "age": 25​​​​​​​,
]

我想按name 分组,然后按country 对每个组进行排序,以遵循另一个集合中指定的明确顺序,例如:

var countryOrder = new List<string>  "UK", "KE", "AU", "USA";

然后我想根据这个顺序只选择每个组的***成员,这样我最终会得到一个名称唯一的对象列表(例如,UKKE 中的一个AliceAUUSA) 但遵循明确定义的优先顺序。

以下是给定示例的预期输出:

output = [
  "name": "Alice", "country": "UK", "age": 30,
  "name": "Bob", "country": "KE", "age": 20,
  "name": "Charlie", "country": "UK", "age": 30,
  "name": "David", "country": "USA", "age": 25,
]

我已尝试使用以下代码:

using System;
using System.Collections.Generic;
using System.Linq;
                    
public class Program

    public static void Main()
    
        var countryOrder =new List<string>  "UK", "KE", "AU", "USA";
        
        var data = new List<ExampleDataClass> 
            new ExampleDataClass  Name = "Alice", Country = "UK", Age = 30 ,
            new ExampleDataClass  Name = "Bob", Country = "KE", Age = 20 ,
            new ExampleDataClass  Name = "Charlie", Country = "UK", Age = 30 ,
            new ExampleDataClass  Name = "Alice", Country = "KE", Age = 40 ,
            new ExampleDataClass  Name = "Bob", Country = "AU", Age = 50 ,
            new ExampleDataClass  Name = "David", Country = "USA", Age = 25 
        ;
        
        var reducedData = data
            .GroupBy(x => x.Name)
            .Select(g => g.OrderBy(item => countryOrder.IndexOf(item.Country)).Min())
            .ToList();
        
        reducedData.ForEach(item => Console.WriteLine(reducedData));
    


class ExampleDataClass

    public string Name  get; set; 
    public string Country  get; set; 
    public int Age  get; set; 

.Select(g =&gt; g.OrderBy(item =&gt; countryOrder.IndexOf(item.Country)).Min()) 给出运行时异常:

Run-time exception (line 50): At least one object must implement IComparable.

Stack Trace:

[System.ArgumentException: At least one object must implement IComparable.]
   at System.Collections.Comparer.Compare(Object a, Object b)
   at System.Collections.Generic.ObjectComparer`1.Compare(T x, T y)
   at System.Linq.Enumerable.Min[TSource](IEnumerable`1 source)
   at Program.<>c__DisplayClassd.<Main>b__9(IGrouping`2 g) :line 50
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Program.Main() :line 49

【问题讨论】:

data是一个JSON数组是否正确? 将其作为 C# 对象的集合处理 data.GroupBy(g => g.name).Select(g => new group = g.Key, items = g.OrderBy(g => g.country)); - 类似这样,但您需要考虑 OrderBy 上的国家/地区收集 您应该尝试解决手头的问题,而不仅仅是告诉我们您想要得到什么。 @Flater 我已经输入了代码尝试和预期的输出 【参考方案1】:

这是我找到的解决方案:


using System;
using System.Collections.Generic;
using System.Linq;
                    
public class Program

    public static void Main()
    
        var countryOrder =new List<string>  "UK", "KE", "AU", "USA";
        
        var data = new List<ExampleDataClass> 
            new ExampleDataClass 
                Name = "Alice",
                Country = "UK",
                Age = 30,
            ,
            
            new ExampleDataClass 
                Name = "Bob",
                Country = "KE",
                Age = 20,
            ,
            
            new ExampleDataClass 
                Name = "Charlie",
                Country = "UK",
                Age = 30,
            ,
            
            new ExampleDataClass 
                Name = "Alice",
                Country = "KE",
                Age = 40,
            ,
            
            new ExampleDataClass 
                Name = "Bob",
                Country = "AU",
                Age = 50,
            ,
            
            new ExampleDataClass 
                Name = "David",
                Country = "USA",
                Age = 25,
            
        ;
        
        var reducedData = data.GroupBy(x => x.Name)
                             .Select(g => g.OrderBy(item => countryOrder.IndexOf(item.Country)).First())
                             .ToList();
        
        foreach (ExampleDataClass item in reducedData) 
        
            Console.WriteLine(string.Format("Name: \"0\", Country: \"1\", Age: 2", item.Name, item.Country, item.Age));
        
    


class ExampleDataClass

    public string Name  get; set; 
    
    public string Country  get; set; 
    
    public int Age  get; set; 

输出

Name: "Alice", Country: "UK", Age: 30
Name: "Bob", Country: "KE", Age: 20
Name: "Charlie", Country: "UK", Age: 30
Name: "David", Country: "USA", Age: 25

链接到 dotnetfiddle https://dotnetfiddle.net/6geHtk

【讨论】:

以上是关于C# LINQ 组按属性集合,然后按列表定义的显式顺序对每个组进行排序[重复]的主要内容,如果未能解决你的问题,请参考以下文章

LinQ 的显式问题

OpenGL着色器的显式与自动属性位置绑定

使用 C# 、 LINQ 进行排序

我可以内联指定我的显式类型比较器吗?

在 C# 中引用类型的内存方面的显式转换解释

使用 C# 出现错误“无法在表中插入标识列的显式值”