根据所提供的相似性将列表压缩成局部组
本文关键字:压缩 列表 成局 相似性 | 更新日期: 2023-09-27 18:12:09
我想为IEnumerable<T>
创建Compact
扩展方法,这将允许我几乎像GroupBy
一样工作,但将创建本地组-这意味着它们应该是两个或多个具有相同键的组,如果它们与具有另一个键的项目分开:
public static IEnumerable<TResult> Compact<T, TKey, TResult>(
this IEnumerable<T> source,
Func<T,TKey> keySelector,
Func<TKey, IEnumerable<T>, TResult> resultSelector);
其中keySelector
是提取每个元素的键,resultSelector
是从每个组创建结果值的函数(与GroupBy重载中的一个完全相同)。
假设我有这样的类和数据:
public class SomeClass
{
public string Name;
public double Value;
public SomeClass(string name, double value)
{
this.Name = name;
this.Value = value;
}
}
var input = new List<SomeClass>() {
new SomeClass("X", 2.0),
new SomeClass("X", 3.0),
new SomeClass("Y", 1.0),
new SomeClass("X", 4.0),
new SomeClass("Z", 0.0),
new SomeClass("Z", 1.0)
};
的用法示例如下:
var output = input.Compact(x => x.Name,
(key, values) => values.Average(v => v.Value));
应该得到一个值为2.5
, 1.0
, 4.0
和0.5
的列表。
我已经创建了几乎工作的方法:
public static IEnumerable<TResult> Compact<T, TKey, TResult>(this IEnumerable<T> source,
Func<T,TKey> keySelector,
Func<TKey, IEnumerable<T>, TResult> resultSelector)
{
if (!source.Any())
yield break;
var comparer = EqualityComparer<TKey>.Default;
TKey previousKey = keySelector(source.First());
List<T> group = new List<T>() { source.First() };
foreach (var item in source.Skip(1))
{
TKey currentKey = keySelector(item);
if (!comparer.Equals(previousKey, currentKey))
{
yield return resultSelector(previousKey, group);
group.Clear();
}
group.Add(item);
previousKey = currentKey;
}
if (group.Any())
{
yield return resultSelector(previousKey, group);
}
}
,但我觉得它可以写得更优雅,更糟糕的是,在以下情况下不能正常工作:
var output = input.Compact(x => x.Name, (key, values) => values);
创建包含四个相同元素的集合,其中仅包含最后一项(具有Z
和0.0
值)。我更希望它是分别有两个、一个、一个和两个项目的列表的列表。
你有什么想法可以改进和纠正吗?
代替group.Clear();
try:
group = new List<T>();
你在你的方法中使用相同的group
。您正在返回它然后清除它,上次您将Z
添加到组时,所有组都具有Z
,因为它们都是相同的。
编辑:您可以通过获得source
的枚举器来改进您的方法,而不是一遍又一遍地使用First
和Skip
:
var enumerator = source.GetEnumerator();
int i = 0;
TKey previousKey = default(TKey);
List<T> group = null;
while (enumerator.MoveNext())
{
if (i == 0)
{
previousKey = keySelector(enumerator.Current);
group = new List<T>() {enumerator.Current};
i++;
}
else
{
TKey currentKey = keySelector(enumerator.Current);
if (!comparer.Equals(previousKey, currentKey))
{
yield return resultSelector(previousKey, group);
group = new List<T>();
}
group.Add(enumerator.Current);
previousKey = currentKey;
}
}