根据所提供的相似性将列表压缩成局部组

本文关键字:压缩 列表 成局 相似性 | 更新日期: 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.00.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);

创建包含四个相同元素的集合,其中仅包含最后一项(具有Z0.0值)。我更希望它是分别有两个、一个、一个和两个项目的列表的列表。

你有什么想法可以改进和纠正吗?

根据所提供的相似性将列表压缩成局部组

代替group.Clear(); try:

group = new List<T>();

你在你的方法中使用相同的group。您正在返回它然后清除它,上次您将Z添加到组时,所有组都具有Z,因为它们都是相同的。

编辑:您可以通过获得source的枚举器来改进您的方法,而不是一遍又一遍地使用FirstSkip:

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