当C#中两个字典的值相等时,在列表中组合/添加字典

时间:2013-07-11 12:31:29

标签: c# dictionary

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

Raw dict可能包含字符串/数字值

例如:

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

原始列表:

Apple West 90

Apple East 10

Apple West 80

已处理清单:

Apple West 170

Apple East 10

考虑一个列表,其中的词典具有相同的产品和区域,我希望通过在“产品”和“产品”中添加“利润”来获得单个词典。 “地区”是相同的。 (即)具有相似项目的词典列表,分组为单个词典,没有任何重复

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

我已经通过Brute-force技术实现了一个逻辑,它消耗了大量的内存和时间。是否有任何方法可以用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: 列表中的所有词典都具有相同的键 不会丢弃任何值。将合并具有相同值的字典。

3 个答案:

答案 0 :(得分: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)));

答案 1 :(得分: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;}
}

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

答案 2 :(得分:0)

换句话说,您想要分组一些字典键,并且您希望通过获取总和来聚合一个键。键是dynamix。 (这听起来像一个动态的报告场景)。

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() }

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

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