List<int>
中,如何检索包含多个重复条目及其值的列表?解决问题最简单的方法是根据元素的值对它们进行分组,然后在组中选择一个代表元素(如果组中有多个元素)。在LINQ中,这可以这样实现:
var query = lst.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(y => y.Key)
.ToList();
如果你想知道元素被重复的次数,你可以使用:
var query = lst.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(y => new { Element = y.Key, Counter = y.Count() })
.ToList();
这将返回一个匿名类型的List
,每个元素都将具有Element
和Counter
属性,以检索所需信息。
最后,如果您要查找字典,可以使用
var query = lst.GroupBy(x => x)
.Where(g => g.Count() > 1)
.ToDictionary(x => x.Key, y => y.Count());
这将返回一个字典,以您的元素作为键,以其重复次数作为值。查找一个可枚举对象是否包含任何重复项:
var anyDuplicate = enumerable.GroupBy(x => x.Key).Any(g => g.Count() > 1);
查找枚举中的所有值是否唯一:
var allUnique = enumerable.GroupBy(x => x.Key).All(g => g.Count() == 1);
仅查找重复的值:
var duplicates = list.GroupBy(x => x.Key).Where(g => g.Count() > 1);
例如。
var list = new[] {1,2,3,1,4,2};
GroupBy
会按照它们的键分组数字,并将重复次数保留在其中。之后,我们只需检查重复多于一次的值。
要仅查找唯一值:
var unique = list.GroupBy(x => x.Key).Where(g => g.Count() == 1);
例如。var list = new[] {1,2,3,1,4,2};
GroupBy
会按照它们的键分组数字,并将重复的次数与之保持在一起。之后,我们只检查那些仅重复了一次的值,也就是唯一的值。
var unique = list.Distinct(x => x)
- Malu MNDistinct
的作用不同,它不仅会返回仅出现一次的值,还会返回出现多次的值(但只返回一次而不是所有多次出现的值);这与答案所指的不同。 - Flater.All(g => g.Count() == 1)
应该改为 .Where(g => g.Count() == 1)
。All
不会像你所说的那样“查找唯一值”,它只是确认整个列表中没有重复项(即所有组的计数都为1)。 - Flater.Any(g => g.Count() > 1)
,这应该是 .Where(g => g.Count() > 1)
。 Any
不会找到重复项本身,它只会确认至少存在一个重复项(即存在一个计数大于 1 的 任何 组)。 - Flater另一种方法是使用 HashSet
:
var hash = new HashSet<int>();
var duplicates = list.Where(i => !hash.Add(i));
如果您希望在重复列表中仅保留唯一值:
var myhash = new HashSet<int>();
var mylist = new List<int>(){1,1,2,2,3,3,3,4,4,4};
var duplicates = mylist.Where(item => !myhash.Add(item)).Distinct().ToList();
这里是相同解决方案的通用扩展方法:
public static class Extensions
{
public static IEnumerable<TSource> GetDuplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector, IEqualityComparer<TKey> comparer)
{
var hash = new HashSet<TKey>(comparer);
return source.Where(item => !hash.Add(selector(item))).ToList();
}
public static IEnumerable<TSource> GetDuplicates<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
{
return source.GetDuplicates(x => x, comparer);
}
public static IEnumerable<TSource> GetDuplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
{
return source.GetDuplicates(selector, null);
}
public static IEnumerable<TSource> GetDuplicates<TSource>(this IEnumerable<TSource> source)
{
return source.GetDuplicates(x => x, null);
}
}
List<int> { 1, 2, 3, 4, 5, 2 }
作为源,结果是一个IEnumerable<int>
,其中只有一个元素的值为1
(正确的重复值应该是2)。 - BCAConsole.WriteLine("Count: {0}", duplicates.Count());
,并且它打印了6
。除非我对该函数的要求有所遗漏,否则结果集中应该只有1个项目。 - BCAToList
以解决此问题,但这意味着该方法在调用时立即执行,而不是在迭代结果时执行。 - HuBeZavar hash = new HashSet<int>();
var duplicates = list.Where(i => !hash.Add(i));
will lead to a list that includes all occurrences of duplicates. So if you have four occurrences of 2 in your list, then your duplicate list will contain three occurrences of 2, since only one of the 2's can be added to the HashSet. If you want your list to contain unique values for each duplicate, use this code instead: var duplicates = mylist.Where(item => !myhash.Add(item)).ToList().Distinct().ToList();
- solid_luffy你可以这样做:
var list = new[] {1,2,3,1,4,2};
var duplicateItems = list.Duplicates();
使用这些扩展方法:
public static class Extensions
{
public static IEnumerable<TSource> Duplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
{
var grouped = source.GroupBy(selector);
var moreThan1 = grouped.Where(i => i.IsMultiple());
return moreThan1.SelectMany(i => i);
}
public static IEnumerable<TSource> Duplicates<TSource, TKey>(this IEnumerable<TSource> source)
{
return source.Duplicates(i => i);
}
public static bool IsMultiple<T>(this IEnumerable<T> source)
{
var enumerator = source.GetEnumerator();
return enumerator.MoveNext() && enumerator.MoveNext();
}
}
在Duplicates方法中使用IsMultiple()比使用Count()更快,因为它不会遍历整个集合。
Count()
]基本上与遍历整个列表不同。Count()
是预先计算的,但遍历整个列表则不是。 - JogiIsMultiple
方法,它很聪明。但是为了其他访问者,请注意:Count() > 1
在今天的情况下肯定比像IsMultiple
或Skip(1).Any()
这样的检查要快。而且不要忘记,在这个实现中我们还没有处理枚举器。另一个快速选项是MoreLINQ的AtLeast
方法。你可以在这里使用AtLeast(2)
。类型检查和获取计数属性比运行枚举器并处理它要快。当然,所有这些都属于微优化,你不应该关心。 - nawfal我创建了一个扩展来响应这个问题,你可以将其包含在你的项目中。我认为这是在搜索列表或Linq中重复项时返回最多的情况。
示例:
//Dummy class to compare in list
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public Person(int id, string name, string surname)
{
this.Id = id;
this.Name = name;
this.Surname = surname;
}
}
//The extention static class
public static class Extention
{
public static IEnumerable<T> getMoreThanOnceRepeated<T>(this IEnumerable<T> extList, Func<T, object> groupProps) where T : class
{ //Return only the second and next reptition
return extList
.GroupBy(groupProps)
.SelectMany(z => z.Skip(1)); //Skip the first occur and return all the others that repeats
}
public static IEnumerable<T> getAllRepeated<T>(this IEnumerable<T> extList, Func<T, object> groupProps) where T : class
{
//Get All the lines that has repeating
return extList
.GroupBy(groupProps)
.Where(z => z.Count() > 1) //Filter only the distinct one
.SelectMany(z => z);//All in where has to be retuned
}
}
//how to use it:
void DuplicateExample()
{
//Populate List
List<Person> PersonsLst = new List<Person>(){
new Person(1,"Ricardo","Figueiredo"), //fist Duplicate to the example
new Person(2,"Ana","Figueiredo"),
new Person(3,"Ricardo","Figueiredo"),//second Duplicate to the example
new Person(4,"Margarida","Figueiredo"),
new Person(5,"Ricardo","Figueiredo")//third Duplicate to the example
};
Console.WriteLine("All:");
PersonsLst.ForEach(z => Console.WriteLine("{0} -> {1} {2}", z.Id, z.Name, z.Surname));
/* OUTPUT:
All:
1 -> Ricardo Figueiredo
2 -> Ana Figueiredo
3 -> Ricardo Figueiredo
4 -> Margarida Figueiredo
5 -> Ricardo Figueiredo
*/
Console.WriteLine("All lines with repeated data");
PersonsLst.getAllRepeated(z => new { z.Name, z.Surname })
.ToList()
.ForEach(z => Console.WriteLine("{0} -> {1} {2}", z.Id, z.Name, z.Surname));
/* OUTPUT:
All lines with repeated data
1 -> Ricardo Figueiredo
3 -> Ricardo Figueiredo
5 -> Ricardo Figueiredo
*/
Console.WriteLine("Only Repeated more than once");
PersonsLst.getMoreThanOnceRepeated(z => new { z.Name, z.Surname })
.ToList()
.ForEach(z => Console.WriteLine("{0} -> {1} {2}", z.Id, z.Name, z.Surname));
/* OUTPUT:
Only Repeated more than once
3 -> Ricardo Figueiredo
5 -> Ricardo Figueiredo
*/
}
有一个答案,但我不明白为什么它不能工作。
var anyDuplicate = enumerable.GroupBy(x => x.Key).Any(g => g.Count() > 1);
在这种情况下,我的解决方案是这样的:
var duplicates = model.list
.GroupBy(s => s.SAME_ID)
.Where(g => g.Count() > 1).Count() > 0;
if(duplicates) {
doSomething();
}
这里有一种其他的方法:
仅针对HasDuplicate
:
bool hasAnyDuplicate = list.Count > list.Distinct().Count;
对于重复值
List<string> duplicates = new List<string>();
duplicates.AddRange(list);
list.Distinct().ToList().ForEach(x => duplicates.Remove(x));
// for unique duplicate values:
duplicates.Distinct():
在MS SQL Server中检查了完整的Linq to SQL扩展的重复函数集。不使用.ToList()或IEnumerable。这些查询在SQL Server中执行而不是在内存中执行。结果仅在内存中返回。
public static class Linq2SqlExtensions {
public class CountOfT<T> {
public T Key { get; set; }
public int Count { get; set; }
}
public static IQueryable<TKey> Duplicates<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
=> source.GroupBy(groupBy).Where(w => w.Count() > 1).Select(s => s.Key);
public static IQueryable<TSource> GetDuplicates<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
=> source.GroupBy(groupBy).Where(w => w.Count() > 1).SelectMany(s => s);
public static IQueryable<CountOfT<TKey>> DuplicatesCounts<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
=> source.GroupBy(groupBy).Where(w => w.Count() > 1).Select(y => new CountOfT<TKey> { Key = y.Key, Count = y.Count() });
public static IQueryable<Tuple<TKey, int>> DuplicatesCountsAsTuble<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
=> source.GroupBy(groupBy).Where(w => w.Count() > 1).Select(s => Tuple.Create(s.Key, s.Count()));
}
这是更简单的方法,不需要使用分组,只需获取区域元素,然后对它们进行迭代,并检查它们在列表中的计数,如果它们计数>1,则表示它出现了多个项目,因此将其添加到Repeteditemlist中。
var mylist = new List<int>() { 1, 1, 2, 3, 3, 3, 4, 4, 4 };
var distList= mylist.Distinct().ToList();
var Repeteditemlist = new List<int>();
foreach (var item in distList)
{
if(mylist.Count(e => e == item) > 1)
{
Repeteditemlist.Add(item);
}
}
foreach (var item in Repeteditemlist)
{
Console.WriteLine(item);
}
代码
for (int i = 0; i < duplicates.Count; i++) {int duplicate = duplicates[i]; duplicatesLocation.Add(duplicate, new List());
for (int k = 0; k < hitsList.Length; k++)
{
if (hitsList[k].Contains(duplicate))
{
duplicatesLocation.ElementAt(i).Value.Add(k);
}
}
// 根据某些规则删除重复项。
}
代码
- Mirko Arcese