【问题标题】:How do I group by in LINQ using multiple criteria?如何在 LINQ 中使用多个条件进行分组?
【发布时间】:2019-05-09 19:33:59
【问题描述】:

有没有办法在 ObservableCollection 中获取所有可能的组合?

我有一个这样的模型:

    public string MyProperty { get; set; }
    public string MyProperty2 { get; set; }
    public string MyProperty3 { get; set; }
    public string MyProperty4 { get; set; }
    public string MyProperty5 { get; set; }
    public string MyProperty6 { get; set; }
    public string MyProperty7 { get; set; }
    public string MyProperty8 { get; set; }

我用电子表格中的数据填充这个模型,但有些值的值为零或空值(我需要排除)。有没有办法让所有可能的组合都具有相同的模式?

例如,所有属性中值不为0的所有组合,以及只有一个属性值为0而其他属性为0时的所有组合等。

到目前为止,我有这样的事情:

var group1 = _sourceStructure.Where(c => c.MyProperty != "0" && c.MyProperty2 != "0" && c.MyProperty3 != "0" && c.MyProperty4 != "0"
        && c.MyProperty5 != "0" && c.MyProperty6 != "0" && c.MyProperty7 != "0" && c.MyProperty8 != "0");

但是我需要使用 30 多个案例来评估,有没有办法使用 LINQ 或其他解决方案获得所有可能的组合?

我想用集合中的值构建一个 SQL 查询,但如果该值为 0 或为空,我不会将该值添加到查询中。我想获得具有相同模式的所有组合,以便能够将具有相同模式的所有项目放在 SQL 中的 IN 中。

输出数据将是这样的:

string query = @"Select field1, field2, field3, fieldn FROM table WHERE "

query = query + "field1 = " + _sourceStructure.MyProperty1;

query = query + "fieldN =  " + _sourceStructure.MyPropertyN;

基本上,我不在乎值是多少。我只需要将具有相同模式的所有可能组合对集合进行分组。

Excel 中原始文件的示例数据:

       MyProperty1 MyPropert2 MyPropertN
Row1      0          1           3
Row2      2          0           6
Row3      0          5           9
Row4      9          9           4
Row5      4          3           6
Row6      0          0           0

在这里,例如,我期望 Row1 和 Row3 将在同一个组中(值不一样但“结构”它是相同的),然后 Row4 和 Row5 将是另一个组,Row6另一个,Row2 另一个。

【问题讨论】:

  • 如果这些是正确的属性名称,您是否考虑过使用 List<String> 而不是离散字段?
  • 您确定分组是您要查找的内容,还是只想查询对象以查找或排除某些对象?分组是不同的东西。
  • @ScottHannen 是的,我只想获取值不同于“0”的字段,但如果该项目有一个属性为“0”,我想获取其他属性,有意义吗?并从所有集合中获取所有可能的组合,将有超过 90k 条记录
  • 我同意@EdPlunkett。如果您有一个列表,那么代码将是 Where(c => c.MyList.All(x => x != "0"))
  • 你能转动你的桌子吗?这使这类查询变得更加容易

标签: c# sql linq


【解决方案1】:

您可以使用反射来获取所有名为MyPropertyn 的属性,其中n 是1 到任何值,然后您可以使用PropertyInfos 的List 来计算填充属性的位掩码,并分组按那个值。

首先我使用的一些扩展方法:

public static class StringExt {
    public static string Past(this string s, string starter) {
        var starterPos = s.IndexOf(starter);
        return starterPos == -1 ? String.Empty : s.Substring(starterPos + starter.Length);
    }
}

public static class NumericExt {
    public static int ToInt<T>(this T obj) => Convert.ToInt32(obj);

    public static int IntPow(this int x, int pow) {
        int ans = 1;

        while (pow != 0) {
            if ((pow & 0x1) == 1)
                ans *= x;
            x *= x;
            pow >>= 1;
        }
        return ans;
    }
}

现在您可以收集感兴趣的属性,排序,然后计算每一行的位掩码:

var myPropInfos = typeof(COC).GetProperties()
                             .Where(pi => pi.Name.StartsWith("MyProperty"))
                             .OrderBy(pi => pi.Name.Past("MyProperty").ToInt()) // just in case properties aren't ordered
                             .ToList();
var GroupedFilters = src.Select(r => new { r, ValuedMask = myPropInfos.Select((pi, p) => pi.GetValue(r).ToString() != "0" ? 2.IntPow(p) : 0).Sum() })
                        .GroupBy(rm => rm.ValuedMask, rm => rm.r);

如果第一个属性不是以整数结尾,您将需要一个测试来在排序中处理它,或者,(特别是如果属性不都以数字结尾)您可以省略@987654326 @ 并使用 GetProperties 返回的任何顺序 - 顺序并不重要。返回的答案是一个 IGrouping 用于每个有价值的属性组合,Key 是位掩码,显示哪些属性被重视。

如果你有超过 31 个属性,你应该改用 long 并创建明显的 LongPow 扩展方法 (2L.LongPow(p) : 0L)。

【讨论】:

    【解决方案2】:

    UNPIVOT 你的表是这样的:

     Observable     Property      Value
         A             1            0
         A             2            1
         A             3            2
         B             1            0
         B             2            0
         B             3            1
         C             1            1
         C             2            2
         C             3            3
    

    所有属性中值不为0的所有组合:

    SELECT Observable
    FROM Table
    GROUP BY Observable
    HAVING SUM(CASE WHEN VALUE = 0 THEN 1 ELSE 0 END) = 0
    

    只有一个属性有值而其他属性为零时的所有组合:

    SELECT Observable
    FROM Table
    GROUP BY Observable
    HAVING COUNT(CASE WHEN VALUE > 0 THEN 1 END) = 1
    

    SQL DEMO

    【讨论】:

      猜你喜欢
      • 2012-11-22
      • 1970-01-01
      • 1970-01-01
      • 2012-02-02
      • 1970-01-01
      • 1970-01-01
      • 2023-01-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多