使用分组依据删除重复项

时间:2013-07-09 18:57:14

标签: c# linq collections duplicates

我正在寻找一种删除重复项的简单方法,而不必实现IComparable类,必须重写GetHashCode等。

我认为这可以通过linq来实现。我有课:

class Person
{
    public string Name;
    public ing Age;
}

我有一个约500人List<Person> someList = new List<Person()

的列表

现在我想删除同名的人,如果有重复,我想保留年龄较大的人。换句话说,如果我有列表:

Name----Age---
Tom,     24  |
Alicia,  22  |
Alicia,  12  |

我想最终:

Name----Age---
Tom,     24  |
Alicia,  22  |

如何使用查询执行此操作?我的列表不长,所以我不想创建哈希集也不想实现IComparable接口。如果我能用linq查询来做这件事会很好。

我认为这可以通过groupBy扩展方法完成,例如:

var people = // the list of Person
person.GroupBy(x=>x.Name).Where(x=>x.Count()>1)
      ...    // select the person that has the greatest age...

4 个答案:

答案 0 :(得分:8)

people
  .GroupBy(p => p.Name)
  .Select(g => g.OrderByDescending(p => p.Age).First())

这将适用于不同的Linq提供商。如果这只是Linq2Objects,速度很重要(通常不是),请考虑使用网络上找到的许多MaxBy扩展之一(这里是Skeet)并替换

g.OrderByDescending(p => p.Age).First()

g.MaxBy(p => p.Age)

答案 1 :(得分:3)

只要您首先创建一个能够从选择器最大的序列中选择项目的辅助函数MaxBy,这可以轻松简单。不幸的是,LINQ中的Max函数不起作用,因为我们想从序列中选择项目,而不是选择的值。

var distinctPeople = people.GroupBy(person => person.Name)
   .Select(group => group.MaxBy(person => person.Age));

然后执行MaxBy

public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector, IComparer<TKey> comparer = null)
{
    comparer = comparer ?? Comparer<TKey>.Default;

    using (var iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
            throw new ArgumentException("Source must have at least one item");

        var maxItem = iterator.Current;
        var maxKey = keySelector(maxItem);

        while (iterator.MoveNext())
        {
            var nextKey = keySelector(iterator.Current);
            if (comparer.Compare(nextKey, maxKey) > 0)
            {
                maxItem = iterator.Current;
                maxKey = nextKey;
            }
        }

        return maxItem;
    }
}

请注意,虽然您可以通过对序列进行排序然后获取第一项来获得相同的结果,但这样做效率通常低于仅使用max函数进行一次传递。

答案 2 :(得分:0)

我更喜欢简单:

var retPeople = new List<Person>;
        foreach (var p in person)
        {
            if(!retPeople.Contains(p))
            {
                retPeople.Add(p);
            }
        }

让人们实施IComparable

答案 3 :(得分:-1)

我摆脱了我的最后一个答案,因为我意识到它太慢而且太复杂了。这是一个更有意义的解决方案

        var peoplewithLargestAgeByName =
            from p in people
            orderby p.Name
            group p by p.Name into peopleByName
            select peopleByName.First ( );

这与@spender提供的解决方案相同,但使用linq语法。