簡體   English   中英

在 C# 中是否有類似於 Addrange() 的 AddUnique 方法用於 alist

[英]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.Equalsobject.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM