【问题标题】:Using Linq to group a list of objects that contains primitives data into a new grouped list of objects使用 Linq 将包含原始数据的对象列表分组到新的对象分组列表中
【发布时间】:2020-04-18 07:59:56
【问题描述】:

我有一个包含不同原始数据类型的对象列表:

List<object> list = new List<object>() { 
  3, "cat", 4.1, 'a', 5, "dog", "horse", 9.1, 'd', 1 
};

我希望能够在上述列表上运行 Linq 查询,该查询按数据类型和顺序将所有元素分组为(字符串、整数、双精度、字符)(如果有意义?)。比如:

list = { "cat","dog", "horse", 3, 5, 1, 4.1, 9.1, 'a', 'd' }

我试过了

Using Linq to group a list of objects into a new grouped list of list of objects

代码

lst
  .GroupBy(x => x.GetType())
  .Select(grp => grp.ToList())
  .ToList()
  .ForEach(group => group.ForEach(item => Console.WriteLine(item)));

有效,但没有提供所需的输出。

输出

 { 3, 5, 1, "cat","dog", "horse",4.1, 9.1, 'a', 'd' }

【问题讨论】:

  • 你的表达式一般都有效,你想要哪个输出?也许只是按类型订购?
  • 基本上你想将你的列表聚合成一个列表。当然,ForEach 表明你掌握了正确的要点。
  • @PavelAnikhouski 我想将输出重新排序为 (string,integer,double,character) 而不是 (integer , string, double , character)
  • 所以你需要使用自定义比较器并使用Sort
  • 如果 (string,integer,double,character) 故意按类型名称降序排序,list.OrderByDescending(x =&gt; x.GetType().Name) 应该这样做

标签: c# list linq


【解决方案1】:

您可以在Select 之后通过降序排列类型名称,它会为您提供所需的输出 - (字符串、整数、双精度、字符)项目

list.GroupBy(x => x.GetType())
    .OrderByDescending(g => g.Key.Name)
    .Select(grp => grp.ToList())
    .ToList()
    .ForEach(group => group.ForEach(Console.WriteLine));

另一种选择是按IsPrimitive 属性OrderBy(g =&gt; g.Key.IsPrimitive) 排序,对于您的特定数据,它还可以提供所需的输出

【讨论】:

    【解决方案2】:

    如果问题是输出中类型的顺序,您可以使用自定义的IComparer 类和您列出的Sort

    基本原理是可以将类型映射到整数分数,然后返回分数之间的比较。

    documentation

    (dotnetFiddle)

    public class StringThenIntThenDoubleThenChar  : Comparer<object> 
    {
        public override int Compare(object x, object y)
        {
            return GetTypeScore(x).CompareTo(GetTypeScore(y));
        }
    
        private int GetTypeScore(object o) 
        {
            var type = o.GetType();
            if (type == typeof(string)) return 0;
            else if (type == typeof(int)) return 1;
            else if (type == typeof(double)) return 2;
            else if (type == typeof(char)) return 3;
            else return 4;
    
            /* Or, if you are using C# 8 :
            return o.GetType() switch
            {
                Type t when t == typeof(string) => 0,
                Type t when t == typeof(int) => 1,
                Type t when t == typeof(double) => 2,
                Type t when t == typeof(char) => 3,
                _ => 4
            };
            */
        }
    }
    

    像这样使用它:

            List<object> list = new List<object>() { 3, "cat", 4.1, 'a', 5, "dog", "horse", 9.1, 'd', 1 };
    
            list.Sort(new StringThenIntThenDoubleThenChar());
            list.ForEach(x => Console.WriteLine(x));
    

    与已经给出的更简单的解决方案(按类型名称排序)相比,优势在于您可以根据需要自定义它。

    您还可以优化比较器,例如如果分数相等,则可以使用默认比较顺序比较它们(以便stringint 等分别在它们之间排序)。

    【讨论】:

      【解决方案3】:

      @Pac0's 答案的类似方法,您可以创建一个方法来决定类型的排序顺序:

      private static int OrderByOnType(object o)
      {
          var type = o.GetType();
      
          if (type == typeof(string))
              return 0;
      
          else if (type == typeof(int)) 
              return 1;
      
          else if (type == typeof(double)) 
              return 2;
      
          else if (type == typeof(char)) 
              return 3;
      
          return 4;
      }
      

      然后将此方法从 LINQ 传递给 Enumerable.OrderBy 以创建新的排序列表:

      var result = list.OrderBy(OrderByOnType);
      
      Console.WriteLine("{ " + string.Join(", ", result) + " }");
      

      或者通过传递您自己的Comparison&lt;T&gt; Delegate,使用List&lt;T&gt;.Sort 对原始列表进行就地排序:

      list.Sort((x, y) => OrderByOnType(x).CompareTo(OrderByOnType(y)));
      
      Console.WriteLine("{ " + string.Join(", ", list) + " }");
      

      也可以封装成单独的方法:

      private static int CompareTwoTypes(object x, object y)
      {
          return OrderByOnType(x).CompareTo(OrderByOnType(y));
      }
      
      list.Sort(CompareTwoTypes);
      
      Console.WriteLine("{ " + string.Join(", ", list) + " }");
      

      所有这些都给出:

      { cat, dog, horse, 3, 5, 1, 4.1, 9.1, a, d }
      

      【讨论】:

        【解决方案4】:

        我认为@Pac0's@RoadRunner 答案的较短版本是在Linq 查询中使用switch 表达式。

                list.OrderBy(x => x  switch
                {
                     string xString=> 0,
                     int xInt=> 1,
                     double xDouble=> 2,
                     char xChar=> 3,
                    _ => -1,
                }).ToList().ForEach(item => Console.WriteLine(item));
        

        【讨论】:

          猜你喜欢
          • 2011-02-11
          • 1970-01-01
          • 1970-01-01
          • 2017-09-18
          • 2013-09-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多