我有一个包含字符串键和整数值的字典。该值表示密钥的出现次数。
如何使用代表前25%值的键和值创建新词典?值的总和应等于或大于所有值的总和。例如,如果我的字典包含5个具有值(5,3,2,1,1)的项目,并且我想要前50%,则新字典将包含值(5,3),因为它们的总和是8,那是&gt ; = 12%的50%。这个词典需要按值降序排序,然后采用前N个,使得它们的总和符合指定的百分比。
此代码为我提供了前N名,但基于已知计数。如何考虑所需的百分比?
var topItemsCount = dictionary.OrderByDescending(entry => entry.Value)
.Take(topN)
.ToDictionary(pair => pair.Key, pair => pair.Value);
答案 0 :(得分:1)
类似的东西:
var topItemsCount = dictionary.OrderByDescending(entry => entry.Value)
.Take(Math.Floor(dictionary.Count * 0.25))
.ToDictionary(pair => pair.Key, pair => pair.Value);
在字典上运行.Count会返回集合中键值对的数量。将Math.Floor舍入到最接近的int。
编辑以反映评论
我可能只是使用一个简单的非linq解决方案来实现你想要的。也许更冗长,但任何人都清楚它的作用:
var total = dictionary.Sum(e => e.Value);
var cutoff = total * 0.5;
var sum = 0;
var pairs = new List<KeyValuePair<string, int>>();
foreach (var pair in dictionary.OrderByDescending(e => e.Value))
{
sum += pair.Value;
pairs.Add(pair);
if (sum > cutoff)
break;
}
dictionary = pairs.ToDictionary(pair => pair.Key, pair => pair.Value);
再修改一次
如果你真的想要更多的linq,你可以试着拿着累积的班级变量。
private static int sum = 0;
static void Main(string[] args)
{
var dictionary = new Dictionary<string, int>()
{
{"1",5},
{"2",3},
{"3",2},
{"4",1},
{"5",1},
};
var total = dictionary.Sum(e => e.Value);
var cutoff = total * 0.5;
var filtered = dictionary.OrderByDescending(e => e.Value)
.TakeWhile(e => Add(e.Value).Item1 < cutoff)
.ToDictionary(pair => pair.Key, pair => pair.Value);
}
private static Tuple<int, int> Add(int x)
{
return Tuple.Create(sum, sum += x);
}
使用返回元组的add函数有点令人费解,因为你在结果中包含了第一个违反截止值的值(即使5 + 3 = 8大于截止6,你仍然包括3 )。
答案 1 :(得分:1)
将问题改为两部分:
问题1看起来像
double percent = inputValue;
double n = dictionary.Values.Sum() * percent;
问题2看起来像:
Dictionary<string, int> newValues = dictionary.OrderByDescending(_ => _.Value)
.Aggregate(
new {sum = 0.0, values = new Dictionary<string, int>()},
(sumValues, kv) =>
{
if (sumValues.sum <= n)
sumValues.values.Add(kv.Key, kv.Value);
return new {sum = sumValues.sum + kv.Value, values = sumValues.values};
},
sumValues => sumValues.values);
您也可以使用for循环和运行总和,但是对于运行范围有限的总计,我喜欢Aggregate函数的紧凑性。这样做的缺点是整个源字典仍然是迭代的。自定义迭代器方法可以解决这个问题。例如:
public static class Extensions
{
public static IEnumerable<TThis> TakeGreaterThan<TThis>(this IEnumerable<TThis> source, Func<TThis, double> valueFunc, double compareTo)
{
double sum = 0.0;
IEnumerable<TThis> orderedSource = source.OrderByDescending(valueFunc);
var enumerator = orderedSource.GetEnumerator();
while (sum <= compareTo && enumerator.MoveNext())
{
yield return enumerator.Current;
sum += valueFunc(enumerator.Current);
}
}
}
用作
Dictionary<string, int> newValues = dictionary.TakeGreaterThan(_ => _.Value, n).ToDictionary(_ => _.Key, _ => _.Value);
答案 2 :(得分:0)
可能是这个吗?
var dictionary = new Dictionary<string, int>()
{
{"1",5},
{"2",3},
{"3",2},
{"4",1},
{"5",1},
};
var max = dictionary.Values.Max();
int percent = 50;
int percentageValue = max*percent /100;
var topItems = dictionary.OrderByDescending(entry => entry.Value)
.TakeWhile(x => x.Value > percentageValue)
.ToDictionary(pair => pair.Key, pair => pair.Value);
foreach (var item in topItems)
{
Console.WriteLine(item.Value);
}
输出:
5
3