Linq 排除子项中的列

Linq Exclude columns in children

我正在尝试以下操作:给定动态对象列表,使用提供的列列表执行 GROUP BY。这是我目前拥有的:

using Newtonsoft.Json;

private static void Main(string[] args)
{
    dynamic[] data = {
        new { A = "A1", B = "B1", C = "C1", D = "D1", E = "E1", F = "F1"},
        new { A = "A1", B = "B1", C = "C1", D = "D1", E = "E1", F = "F1", G = "G1"},
        new { A = "A1", B = "B1", C = "C1", D = "D1", E = "E1", H = "H1", I = "I1"}
    };

    var newList = data.GroupBy(row => new { row.A, row.B, row.C, row.D, row.E })
        .Select(
            y =>
                new
                {
                    A = y.Key.A, B = y.Key.B, C = y.Key.C, D = y.Key.D, E = y.Key.E,
                    Children = y.ToList()
                });

    var str = JsonConvert.SerializeObject(newList);

    Console.Read();
}

这符合我的需要。但是,我想去掉一些作为 Children 字段的一部分出现的附加列。这是我现在为 str:

得到的输出
[
   {
      "A":"A1",
      "B":"B1",
      "C":"C1",
      "D":"D1",
      "E":"E1",
      "Children":[
         {
            "A":"A1",
            "B":"B1",
            "C":"C1",
            "D":"D1",
            "E":"E1",
            "F":"F1"
         },
         {
            "A":"A1",
            "B":"B1",
            "C":"C1",
            "D":"D1",
            "E":"E1",
            "F":"F1",
            "G":"G1"
         },
         {
            "A":"A1",
            "B":"B1",
            "C":"C1",
            "D":"D1",
            "E":"E1",
            "H":"H1",
            "I":"I1"
         }
      ]
   }
]

我理想中想要的是以下内容:

[
   {
      "A":"A1",
      "B":"B1",
      "C":"C1",
      "D":"D1",
      "E":"E1",
      "Children":[
         {
            "E":"E1",
            "F":"F1"
         },
         {
            "F":"F1",
            "G":"G1"
         },
         {
            "H":"H1",
            "I":"I1"
         }
      ]
   }
]

关于如何实现这一点有什么建议吗?我所知道的是,对象之间可能存在一定数量的通用字段(例如 A、B、C、D、E),但无法控制其他字段(例如 F、G、H 等)

您可以使用 ExpandoObject class 仅存储每个项目的唯一属性并反映到 select 这些唯一属性。

这就是我设法在 LinqPad 中起步的原因:

dynamic[] data =
{
    new { A = "A1", B = "B1", C = "C1", D = "D1", E = "E1", F = "F1" },
    new { A = "A1", B = "B1", C = "C1", D = "D1", E = "E1", F = "F1", G = "G1" },
    new { A = "A1", B = "B1", C = "C1", D = "D1", E = "E1", H = "H1", I = "I1" }
};

var keyProperties = new[] { "A", "B", "C", "D", "E" };
var newList = data.GroupBy(row => new { row.A, row.B, row.C, row.D, row.E }).Select(
    y => new
         {
             A = y.Key.A,
             B = y.Key.B,
             C = y.Key.C,
             D = y.Key.D,
             E = y.Key.E,
             Children = y.Select(
                 item =>
                 {
                     IEnumerable<dynamic> itemProps = item.GetType().GetProperties();
                     List<dynamic> properties =
                         itemProps.Where(p => !Enumerable.Contains(keyProperties, p.Name)).ToList();
                     var result = new ExpandoObject();
                     foreach (var property in properties)
                     {
                         var value = property.GetValue(item, null);
                         if (value != null)
                         {
                             ((IDictionary<string, object>)result).Add(property.Name, value);
                         }
                     }
                     return result;
                 }).ToList()
         });
var str = JsonConvert.SerializeObject(newList);

结果是:

[
  {
    "A": "A1",
    "B": "B1",
    "C": "C1",
    "D": "D1",
    "E": "E1",
    "Children": [
      {
        "F": "F1"
      },
      {
        "F": "F1",
        "G": "G1"
      },
      {
        "H": "H1",
        "I": "I1"
      }
    ]
  }
]

编辑

要在子级别合并元素,您需要稍微更改投影。

首先,Select 变成了 SelectMany 到 return KeyValuePair<string, object> 的集合,随后将按名称分组并连接成一个数组:

Children = y.SelectMany(item =>
{
    IEnumerable<dynamic> itemProps = item.GetType().GetProperties();
    List<dynamic> properties = itemProps.Where(p => !Enumerable.Contains(keyProperties,p.Name)).ToList();
    var result = new ExpandoObject();
    foreach(var property in properties)
    {
        var value = property.GetValue(item, null);
        if(value != null)
            ((IDictionary<string, object>)result).Add(property.Name, value);
    }
    return (IDictionary<string, object>)result;
}).GroupBy(prop => prop.Key, prop => prop.Value)
.Select(g => 
{
    var result = new ExpandoObject();
    ((IDictionary<string, object>)result).Add(g.Key, g.ToArray());
    return result;
})
.ToList()

这不是最优雅的代码,但它应该可以完成工作。