我想从原始列表字典值(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: 列表中的所有词典都具有相同的键 不会丢弃任何值。将合并具有相同值的字典。
答案 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
进行聚合。