嵌套 IEnumerable<IDictionary> 到不同类型的嵌套 IEnumerable<IDictionary> 的映射

Posted

技术标签:

【中文标题】嵌套 IEnumerable<IDictionary> 到不同类型的嵌套 IEnumerable<IDictionary> 的映射【英文标题】:Mapping of nested IEnumerable<IDictionary> to a different type of nested IEnumerable<IDictionary> 【发布时间】:2021-04-25 02:32:04 【问题描述】:

我需要做一些“奇怪”的映射,而且我尝试了很多事情都没有接近,所以我不确定这是否可能。

我有这个“rowDefinition”:这就像最终数据应该是什么样子的合同,“1A”和东西是我将从服务中获得的数据的 ID

var rowDefinitions = new Dictionary<string, IEnumerable<IDictionary<string, object>>>
        
            ["Managers"] = new List<IDictionary<string, object>>
            
                new Dictionary<string, object>
                
                    ["MANAGER_NAME"] = "1A",
                    ["WEBSITE"] = "3A",
                    ["NIP"] = "4A",
                    ["AUM"] = "5A"
                
            ,
            ["Funds"] = new List<IDictionary<string, object>>
            
                new Dictionary<string, object>
                
                    ["FUND_NAME"] = "6A",
                    ["CURRENCY"] = "2A",
                
            
        ;

我有这些数据来自这样的服务,每个字典都代表上述结构的一种行

var dataFromService = new List<IDictionary<string, object>>
        
            new Dictionary<string, object>
            
                ["1A"] = "manager name 1",
                ["3A"] = "website 1",
                ["4A"] = "ni professionals 1",
                ["5A"] = "aum 1"
            ,

            new Dictionary<string, object>
            
                ["1A"] = "manager name 2",
                ["3A"] = "website 2",
                ["4A"] = "other ni professional 2",
                ["5A"] = "one more aum 2"
            ,
            new Dictionary<string, object>
            
                ["6A"] = "fund name 4",
                ["2A"] = "currecy 4",
            ,
            new Dictionary<string, object>
            
                ["6A"] = "fund name 5",
                ["2A"] = "currecy 5",
            ,
            new Dictionary<string, object>
            
                ["6A"] = "fund name 6",
                ["2A"] = "currecy 6",
            ,
        ;

我需要根据“rowDefinition”映射这些数据,得到类似这样的东西

var expectedResult = new Dictionary<string, IEnumerable<IDictionary<string, object>>>
        
            ["Managers"] = new List<IDictionary<string, object>>
            
                new Dictionary<string, object>
                
                    ["MANAGER_NAME"] = "manager name 1",
                    ["WEBSITE"] = "website 1",
                    ["NIP"] = "ni professionals 1",
                    ["AUM"] = "aum 1"
                ,
                new Dictionary<string, object>
                
                    ["MANAGER_NAME"] = "manager name 2",
                    ["WEBSITE"] = "website 2",
                    ["NIP"] = "other ni professional 2",
                    ["AUM"] = "one more aum 2"
                
            ,
            ["Funds"] = new List<IDictionary<string, object>>
            
                new Dictionary<string, object>
                
                    ["FUND_NAME"] = "fund name 4",
                    ["CURRENCY"] = "currency 4"
                ,
                 new Dictionary<string, object>
                
                    ["FUND_NAME"] = "fund name 5",
                    ["CURRENCY"] = "currency 5"
                ,
                  new Dictionary<string, object>
                
                    ["FUND_NAME"] = "fund name 6",
                    ["CURRENCY"] = "currency 6"
                
            
        ;

["Managers"] 或 ["Funds"] 中的 RowDefinitions 可以有更多的键值对,并且可以有其他的东西,例如 ["Potato"],简而言之,我应该能够修改 rowDefinitions,当然我回来的日期将包括相应的信息

这种工作,但看起来很丑(而且肯定效率不高),我认为这可以用 LINQ 完成,但不知道如何

 private void UglyWay(List<IDictionary<string, object>> dataScopeWithDataPoints, IDictionary<string, IEnumerable<IDictionary<string, object>>> rowDefinitions)
    
        var excelData = new Dictionary<string, List<IDictionary<string, object>>>();
        foreach (var dataScope in dataScopeWithDataPoints)
        
            foreach (var excelRowDefinition in rowDefinitions)
            
                var rowDefinition = excelRowDefinition.Value.First();
                var row = new Dictionary<string, object>();
                var found = false;

                foreach (var dataPoint in rowDefinition)
                
                    if (dataScope.ContainsKey(dataPoint.Value.ToString()))
                    
                        row[dataPoint.Key] = dataScope[dataPoint.Value.ToString()];
                        found = true;
                    
                

                if (found)
                
                    if (!excelData.ContainsKey(excelRowDefinition.Key))
                    
                        excelData[excelRowDefinition.Key] = new List<IDictionary<string, object>>();
                    

                    excelData[excelRowDefinition.Key].Add(row);
                
            
        

        return;
    

如果您需要任何额外信息,请告诉我。

【问题讨论】:

为什么你的行定义有一个字典列表来定义每个?那里只有一个条目是可能的,对吗?数据就是这样来的吗? 您好,谢谢您的回复...对于rowDefinition,是的,只有一个条目是可能的...。现在对于“expectedResult”(与rowDefinition相同的类型)可以有多个条目...我可以从 rowDefinition 中删除这个字典列表,我不能碰巧的是“dataFromService”和“expectedResult” 我能问你点别的吗?如果在 "rowDefinitions" 中,["Manager"] 键还包含 ["Funds"] 的一个字段,如下所示:["Managers"] = new List> new Dictionary ["MANAGER_NAME"] = "1A", ["WEBSITE"] = "3A", ["CURRENCY"] = "2A", [ "NIP"] = "4A", ["AUM"] = "5A" ,...所以货币将同时在基金和经理... 那么问题将是如何选择与给定数据字典匹配的行定义:您可以计算有多少匹配,或者指定特定字段作为决定匹配,或者确保所有字段都匹配(什么如果一个行定义是另一个行定义的子集?)。 我添加了一个验证行定义中的所有字段名称是否与数据项匹配的示例。 【参考方案1】:

使用行定义,放入更实用的格式,可以提取每个匹配的数据字典并重新映射:

var ans = rowDefinitions.Select(rd => new  rd.Key, Definition = rd.Value.First().Select(kv => new  kv.Key, Value = kv.Value.ToString() ) )
                        .ToDictionary(rd => rd.Key,
                                      rd => dataFromService.Where(d => d.ContainsKey(rd.Definition.First().Value))
                                                           .Select(d => rd.Definition.ToDictionary(def => def.Key, def => d[def.Value]))
                                                           .ToList()
                        );

注意:这会创建 Dictionary&lt;string, List&lt;Dictionary&lt;string, object&gt;&gt;&gt; 而不是 Dictionary&lt;string, IEnumerable&lt;IDictionary&lt;string, object&gt;&gt;&gt;。如果您确实需要IDictionaryIEnumerable,您可以将dataFromService 创建的值转换为List&lt;&gt;

代码如下:

var ans = rowDefinitions.Select(rd => new  rd.Key, Definition = rd.Value.First().Select(kv => new  kv.Key, Value = kv.Value.ToString() ) )
                        .ToDictionary(rd => rd.Key,
                                      rd => dataFromService.Where(d => d.ContainsKey(rd.Definition.First().Value))
                                                           .Select(d => (IDictionary<string,object>)rd.Definition.ToDictionary(def => def.Key, def => d[def.Value]))
                                                           .ToList()
                                                           .AsEnumerable()
                                                           
                        );

注意:dataFromService 中的缺失值将导致异常。

如果行定义可能重叠,您可以选择具有所有匹配字段名称的行定义,而不是只测试一个:

var ans2 = rowDefinitions.Select(rd => new  rd.Key, Definition = rd.Value.First().Select(kv => new  kv.Key, Value = kv.Value.ToString() ).ToList() )
                         .ToDictionary(rd => rd.Key,
                                       rd => dataFromService.Where(d => rd.Definition.Count == d.Keys.Count && rd.Definition.All(dkv => d.ContainsKey(dkv.Value)))
                                                            .Select(d => (IDictionary<string,object>)rd.Definition.ToDictionary(def => def.Key, def => d[def.Value]))
                                                            .ToList()
                                                            .AsEnumerable()

                         );

【讨论】:

这简直太完美了......我不在乎缺失值,因为它们都会在那里......非常非常感谢!......

以上是关于嵌套 IEnumerable<IDictionary> 到不同类型的嵌套 IEnumerable<IDictionary> 的映射的主要内容,如果未能解决你的问题,请参考以下文章

Protobuf-net:嵌套的 IEnumerable 对象

Lambda语句的嵌套

从 User 表中获取嵌套的 UserRoles

如何在 Linq C# 中使用嵌套字典对列表进行排序?

将 IEnumerable<Ienumerable<T>> 转换为 Dictionary<key,IEnumerable<T>>

C# LINQ 优雅地将 IEnumerable<IGrouping> 转换为 IEnumerable<IEnumerable>?