1

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);

生のリスト:

アップル ウエスト 90

アップルイースト 10

アップル ウエスト 80

処理済みリスト:

アップル ウェスト 170

アップルイースト 10

同じ製品と地域を持つ辞書のリストを考えてみましょう。「製品」と「地域」が同じ場合、「利益」を追加して単一の辞書が必要です。(ie) 重複のない単一の辞書としてグループ化された類似の項目を持つ辞書のリスト

注 : raw リストは 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 つの辞書を結合することになっています。結合しながら数値を追加します。

編集 2: リスト内のすべての辞書は同じキーを持ちます。値は破棄されません。同じ値を持つ辞書は結合されます。

4

3 に答える 3

1

So, you have an

IEnumerable<IDictionary<string, object>>

and you want to merge the dictionaries, based on some key set.

You need to now which keys of the dictionary form the keyset, so you can group the dictionaries appropriately.

You also need need a delegate function to aggragate each of the non key set values.

On that basis, you need a function like this, to do all the work,

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;
    }
}

So, if working with your example data, you'd call it like this

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

If your values are actually string representations of doubles, you might prepare you aggregators like this,

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

In other words, there are some dictionary keys that you want to group on and there is one key that you want to aggregate by taking the sum. The keys are dynamix. (This sound like a dynamic reporting scenario).

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

Hope that helps. It surely contains bugs but you can probably fix them.

The trick was to first pull out the grouping keys and values out of the dictionary, then use a standard LINQ GroupBy to aggregate.

于 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 に答える