c# - 使用 LINQ 选择不同列的唯一值

标签 c# linq

我有一个包含多个列的表(ex 的订单)。

products    categories  subcategories
--------------------------------------

prod1       cat1        sub1
prod1       cat2        sub2
prod2       cat3        sub6
prod1       cat1        sub1
prod5       cat2        sub8
prod2       cat1        sub1
prod1       cat7        sub3
prod8       cat2        sub2
prod2       cat3        sub1

现在我可以编写三个不同的查询来获取不同的值

var prod = (from p in _context.orders select p.products).ToList().Distinct();

同样我可以写给别人。

现在我需要在单个查询中获取每一列的不同值,结果需要看起来像

products    categories  subcategories
--------------------------------------

prod1       cat1        sub1
prod2       cat2        sub2
prod5       cat3        sub6
prod8       cat7        sub8
                        sub3

我的唯一字段的 ClassType 看起来像这样

public class UniqueProductFields
{
    public IEnumerable<string> Products { get; set; }
    public IEnumerable<string> Categories { get; set; }
    public IEnumerable<string> Subcategories { get; set; }
}   

不确定如何以有效的方式执行此操作,这样我就不必编写三个方法。该表在数据库中(因此需要优化)

谢谢!

最佳答案

使用Linq是绝对不变的要求吗?为什么需要在单个查询中返回它?

建议:使用SQL。它可以在单个查询中完成,但您不会喜欢该查询。我假设使用 SQL Server(对于其他 DBMS 可以采用不同的方式)。

WITH V AS (
   SELECT DISTINCT
      V.*
   FROM
      Orders O
      CROSS APPLY (
         VALUES (1, O.Products), (2, O.Categories), (3, O.Subcategories)
      ) V (Which, Value)
),
Nums AS (
   SELECT
      Num = Row_Number() OVER (PARTITION BY V.Which ORDER BY V.Value),
      V.Which,
      V.Value
   FROM
      V
)
SELECT
   Products = P.[1],
   Categories = P.[2],
   Subcategories = P.[3]
FROM
   Nums N
   PIVOT (Max(N.Value) FOR N.Which IN ([1], [2], [3])) P
;

See this working at db<>fiddle

输出:

Products  Categories  Subcategories
--------  ----------  -------------
prod1     cat1        sub1
prod2     cat2        sub2
prod5     cat3        sub3
prod8     cat7        sub6
null      null        sub8

如果您一定要使用 Linq,那么我无法在查询式语法方面为您提供帮助。我只知道 C# 代码风格的语法,但这里有一个尝试。不幸的是,我认为这对你没有任何好处,因为我不得不使用一些非常时髦的东西来让它工作。它使用与上面的 SQL 查询基本相同的技术,只是,在 Linq 中没有 PIVOT 的等效项,并且除了自定义类之外没有真正的自然行对象。

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

public class Program {
    public static void Main() {
        var data = new List<Order> {
            new Order("prod1", "cat1", "sub1"),
            new Order("prod1", "cat2", "sub2"),
            new Order("prod2", "cat3", "sub6"),
            new Order("prod1", "cat1", "sub1"),
            new Order("prod5", "cat2", "sub8"),
            new Order("prod2", "cat1", "sub1"),
            new Order("prod1", "cat7", "sub3"),
            new Order("prod8", "cat2", "sub2"),
            new Order("prod2", "cat3", "sub1")
        };
        int max = 0;
        var items = data
            .SelectMany(o => new List<KeyValuePair<int, string>> {
                new KeyValuePair<int, string>(1, o.Products),
                new KeyValuePair<int, string>(2, o.Categories),
                new KeyValuePair<int, string>(3, o.Subcategories)
            })
            .Distinct()
            .GroupBy(d => d.Key)
            .Select(g => {
                var l = g.Select(d => d.Value).ToList();
                max = Math.Max(max, l.Count);
                return l;
            })
            .ToList();
        Enumerable
            .Range(0, max)
            .Select(i => new {
                p = items[0].ItemAtOrDefault(i, null),
                c = items[1].ItemAtOrDefault(i, null),
                s = items[2].ItemAtOrDefault(i, null)
            })
            .ToList()
            .ForEach(row => Console.WriteLine($"p: {row.p}, c: {row.c}, s: {row.s}"));
    }
}

public static class ListExtensions {
    public static T ItemAtOrDefault<T>(this List<T> list, int index, T defaultValue)
        => index >= list.Count ? defaultValue : list[index];
}

public class Order {
    public Order(string products, string categories, string subcategories) {
        Products = products;
        Categories = categories;
        Subcategories = subcategories;
    }
    public string Products { get; set; }
    public string Categories { get; set; }
    public string Subcategories { get; set; }
}

我想我们可以交换这个

.Select(i => new {
   p = items[0].ItemAtOrDefault(i, null),
   c = items[1].ItemAtOrDefault(i, null),
   s = items[2].ItemAtOrDefault(i, null)
})

为此:

.Select(i => new Order(
   items[0].ItemAtOrDefault(i, null),
   items[1].ItemAtOrDefault(i, null),
   items[2].ItemAtOrDefault(i, null)
))

然后在输出部分使用该类的属性。

关于c# - 使用 LINQ 选择不同列的唯一值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50126052/

相关文章:

c# - 显式 Int 转换溢出

c# - Compact Framework 中的深度克隆

linq - 动态 LINQ 和动态 Lambda 表达式?

c# - 从字典中提取信息

c# - 使用 Linq 关联防止空异常

c# - 仅在构造函数中设置类泛型值

c# - 依赖注入(inject)的方式

java - 旋转对象以放置在二维数组中

c# - Linq 自定义排序

c# - LINQ 查询未检索保存的数据