[英]Is there an AddUnique method similar to Addrange() for alist in C#
我在 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
類型本身( 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)));
無 Linq 選項! 一個不同的選項,以防程序員不能或不想使用 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(...)。
假設您的 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); });
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.