是否有类似于c#中list的AddUnique()方法?
本文关键字:AddUnique 方法 list 类似于 是否 | 更新日期: 2023-09-27 18:18:51
我有一个c#列表:
var list = new List<Car>();
list.AddRange(GetGreenCars());
list.AddRange(GetBigCars());
list.AddRange(GetSmallCars());
问题是一些相同的汽车在不同的函数中返回,我不希望它们在列表中多次出现。每辆车都有一个唯一的Name属性。无论如何,我可以有像上面这样的东西,但只会添加项目,如果他们是唯一的?
一种选择是添加它们并删除重复的:
var list = new List<Car>();
list.AddRange(GetGreenCars());
list.AddRange(GetBigCars());
list.AddRange(GetSmallCars());
list = list.Distinct().ToList();
另一个选项是:
public static void AddUnique<T>( this IList<T> self, IEnumerable<T> items )
{
foreach(var item in items)
if(!self.Contains(item))
self.Add(item);
}
var list = new List<Car>();
list.AddUnique(GetGreenCars());
list.AddUnique(GetBigCars());
list.AddUnique(GetSmallCars());
A List<T>
似乎不是这里合适的集合。你可能想要一个ISet<T>
的实现,如HashSet<T>
(或SortedSet<T>
,如果你需要排序)。
为了允许这一点,你需要编写一个IEqualityComparer<T>
实现,根据Name
属性定义汽车之间的相等性。如果这是car-等式的"规范"定义,您还可以考虑直接将该定义构建到Car
类型本身(object.Equals
, object.GetHashCode
,理想情况下也实现IEquatable<T>
)。
如果您为Car
重写.Equals()
方法以确定一个汽车对象与另一个汽车对象相同,则无需编写扩展方法,以下操作就可以工作。
var list = new List<Car>();
list.AddRange(GetGreenCars()?.Except(list) ?? new List<Car>());
list.AddRange(GetBigCars()?.Except(list) ?? new List<Car>());
list.AddRange(GetSmallCars()?.Except(list) ?? new List<Car>());
使用Linq:
public static void AddUnique<T>(this IList<T> self, IEnumerable<T> items)
{
self.AddRange(
items.Where(x => self.FirstOrDefault(y => y.Name == x.Name) ==
null).ToList());
}
var list = new List<Car>();
list.AddUnique(GetGreenCars());
list.AddUnique(GetBigCars());
list.AddUnique(GetSmallCars());
我创建了一个扩展方法,它只向从IEnumerable<T>
实现ICollection<T>
(包括List<T>
)的任何东西添加唯一的值。与使用List<T>.Contains()
的实现不同,此方法允许您指定一个lambda表达式来确定两个项是否相同。
/// <summary>
/// Adds only items that do not exist in source. May be very slow for large collections and some types of source.
/// </summary>
/// <typeparam name="T">Type in the collection.</typeparam>
/// <param name="source">Source collection</param>
/// <param name="predicate">Predicate to determine whether a new item is already in source.</param>
/// <param name="items">New items.</param>
public static void AddUniqueBy<T>(this ICollection<T> source, Func<T, T, bool> predicate, IEnumerable<T> items)
{
foreach (T item in items)
{
bool existsInSource = source.Where(s => predicate(s, item)).Any();
if (!existsInSource) source.Add(item);
}
}
用法:
source.AddUniqueBy<Foo>((s, i) => s.Id == i.Id, items);
如果你想比较一个属性(在这个例子中是id),这应该可以工作
var list = new List<string>();
list.AddRange(GetGreenCars().Where(greencar => !list.Contains(greencar, car => car.id == greencar.id)));
list.AddRange(GetBigCars().Where(bigcar => !list.Contains(bigcar, car => car.id == bigcar.id)));
list.AddRange(GetSmallCars().Where(smallcar => !list.Contains(smallcar, car => car.id == smallcar.id)));
假设您的Get*Cars()返回汽车列表,另一个选项可能是:
var list = new List<Car>();
GetGreenCars().ForEach(c => { if (!list.Contains(c)) list.Add(c); });
GetBigCars().ForEach(c => { if (!list.Contains(c)) list.Add(c); });
GetSmallCars().ForEach(c => { if (!list.Contains(c)) list.Add(c); });
lin_less选项!如果程序员不能或不想使用Linq,这是一个不同的选择。
var list = new List<Car>();
list.AddRange(GetGreenCars().FindAll((x) => !list.Contains(x)));
list.AddRange(GetBigCars().FindAll((x) => !list.Contains(x)));
list.AddRange(GetSmallCars().FindAll((x) => !list.Contains(x)));
如果像上面的例子那样初始列表为空,实际上可以避免在第一个AddRange()上使用FindAll(…)