1

我想List<Dictionary<String,Object>>从原始列表字典值( )中提取处理后的字典值列表( List<Dictionary<String,Object>>)。

原始字典可能包含字符串/数值

例如:

Dictionary<String, Object> rawListDict = new Dictionary<String, Object>();
rawListDict.Add("Product","Apple");
rawListDict.Add("Region", "West");
rawListDict.Add("Profit", 90);

原始清单:

苹果西 90

苹果东 10

苹果西 80

处理清单:

苹果西 170

苹果东 10

考虑一个包含具有相同产品和区域的字典的列表,我想要一个字典,当“产品”和“区域”相同时添加“利润”。(即)具有相似项目的字典列表被分组为单个字典,没有任何重复

注意:原始列表可以超过 30K 条目。:-(

我已经通过蛮力技术实现了一个逻辑,它消耗了大量的内存和时间。有没有办法以 LINQ 风格或任何其他方法来减少时间和内存?

编辑:我更喜欢字典,因为成员/键的数量仅在运行时才知道。

我已经实现的代码:

                    //Get fields which could be used for combining values
                    var nonMeasurableFields = report.datagrid_fields.
                        Where(field => field.dataType.Equals(ImFieldDatatype.STRING_VALUE) || field.dataType.Equals(ImFieldDatatype.DATE_VALUE)).
                        Select(field => field.name).ToList();

                    if (nonMeasurableFields != null && nonMeasurableFields.Count > 0)
                    {
                        #region Outer For Loop

                        for (int index = 0; index < processedData.Count; index++)
                        {
                            var baseDict = processedData.ElementAt(index);

                            Dictionary<String, Object> compareDict = null;

                            #region Recursive Loop

                            for (int recursiveIndex = index + 1; recursiveIndex < processedData.Count; recursiveIndex++)
                            {
                                compareDict = processedData.ElementAt(recursiveIndex);

                                int matchesCount = 0;

                                #region comparison logic

                                foreach (var key in nonMeasurableFields)
                                {
                                    var baseDictValue = baseDict[key];
                                    var compareDictValue = compareDict[key];

                                    if (baseDictValue == null && compareDictValue == null)
                                    {
                                        matchesCount++;
                                    }
                                    else
                                    {
                                        if (baseDictValue != null && compareDictValue == null)
                                        {
                                            matchesCount = 0;
                                        }
                                        else if (baseDictValue == null && compareDictValue != null)
                                        {
                                            matchesCount = 0;
                                        }
                                        else if (baseDictValue != null && compareDictValue != null)
                                        {
                                            if (baseDictValue.Equals(compareDictValue))
                                            {
                                                matchesCount++;
                                            }
                                            else
                                            {
                                                matchesCount = 0;
                                            }
                                        }

                                    }
                                }

                                #endregion

                                #region If Match -- Combine

                                if (matchesCount == nonMeasurableFields.Count)
                                {
                                    #region combine logic

                                    // Combine the two dictionary .. 

                                    processedData.Remove(baseDict);
                                    processedData.Remove(compareDict);

                                    // combine the base and compare dict

                                    Dictionary<String, Object> combinedDict = new Dictionary<string, object>();

                                    var keyNeededInDict = baseDict.Keys.ToList();

                                    foreach (var key in keyNeededInDict.ToList())
                                    {
                                        if (nonMeasurableFields.Contains(key))
                                        {
                                            combinedDict.Add(key, baseDict[key]);
                                        }
                                        else
                                        {
                                            Object value = Convert.ToDouble(baseDict[key]) + Convert.ToDouble(compareDict[key]);

                                            combinedDict.Add(key, value);
                                        }
                                    }

                                    processedData.Add(combinedDict);

                                    index = -1; // Resetting the looping index so that the merging works for all values
                                    recursiveIndex = -1; // Ensuring all the values are considered at least once whenever 
                                    // a change is made to the list (i.e merging the dict)
                                    break;
                                    #endregion
                                }
                                else
                                {
                                    // No matches
                                    // continue to next
                                }

                                #endregion
                            }

                            #endregion
                        }

                        #endregion
                    }

注意: 我将知道哪个键(键的值)是字符串类型和数字类型的信息。提供的示例仅用于演示目的。键和值仅在运行时才知道。如果字符串值相等,我应该合并两个字典。我将在合并时添加数值。

编辑2: 列表中的所有字典都将具有相同的键没有值将被丢弃。具有相同值的字典将被合并。

4

3 回答 3

1

所以,你有一个

IEnumerable<IDictionary<string, object>>

并且您想根据某些键集合并字典。

您现在需要字典的哪些键构成键集,以便您可以适当地对字典进行分组。

您还需要一个委托函数来聚合每个非键集值。

在此基础上,你需要一个这样的函数来完成所有的工作,

IEnumerable<IDictionary<string, object>> Merger(
        IEnumerable<IDictionary<string, object>> source,
        IEnumerable<string> keys,
        IDictionary<string, Func<IEnumerable<object>, object>> aggregators)
{
    var grouped = source.GroupBy(d => string.Join("|", keys.Select(k => d[k])));

    foreach(var g in grouped)
    {
        var result = new Dictionary<string, object>();
        var first = g.First();
        foreach(var key in keys)
        {
            result.Add(key, first[key]);
        }

        foreach(var a in aggregators)
        {
            result.Add(a.Key, a.Value(g.Select(i => i[a.Key])));
        }

        yield return result;
    }
}

因此,如果使用您的示例数据,您可以这样称呼它

var processedDictionaries = Merger(
    rawListDict,
    new[] { "Product", "Region" },
    new Dictionary<string, Func<IEnumerable<object>, object>>
        {
            { "Profit", objects => objects.Cast<int>().Sum() }
        });

如果您的值实际上是双精度的字符串表示形式,您可以像这样准备聚合器,

var aggregators = new Dictionary<string, Func<IEnumerable<object>, object>>();
aggregators.Add(
     "Profit",
     objects => objects.Cast<string>().Sum(s => double.Parse(s)));
于 2013-07-11T13:27:48.537 回答
0

换句话说,您想要对一些字典键进行分组,并且您想要通过求和来聚合一个键。键是动态混合的。(这听起来像是一个动态报告场景)。

var groupingKeys = new [] { "Product", "Region" };
var aggKey = "Profit";
List<Dictionary<String,Object>> rows = GetRows(); //provided

var results =
from r in rows
let groupingValues = groupingKeys.Select(key => r[key]).ToArray()
let groupingString = string.Join("|", groupingValues) //HACK - you better use an array comparer
let aggValue = Convert.ToInt32(r[aggKey])
group aggValue by groupingString into g
select new { Key = g.Key, Sum = g.Sum() }

希望有帮助。它肯定包含错误,但您可能可以修复它们。

诀窍是首先从字典中提取分组键和值,然后使用标准 LINQGroupBy进行聚合。

于 2013-07-11T13:28:09.110 回答
0
var lookup = dicList.ToLookup(x => new{
                                    Product = x["Product"], 
                                    Region = x["Region"]});
var condensedDicList = lookup
       .Select(x => new Dictionary<string, object>(){
                         {"Product",x.Key.Product},
                         {"Region",x.Key.Region},
                         {"Profit",x.Sum(d=>(int)d["Profit"])}
        })
       .ToList();

但说真的......为什么不写一堂课

class MyData
{
    public string Product{get;set;}
    public string Region{get;set;}
    public int Profit{get;set;}
}

并为自己节省大量的球痛。

于 2013-07-11T12:47:48.700 回答