Add items to a collection if the collection does NOT already contain it by comparing a property of the items?
Basically, how do I make it so I can do something similar to: CurrentCollection.Contains(...)
, except by comparing if the item's property is already in the collection?
public class Foo开发者_JAVA百科
{
public Int32 bar;
}
ICollection<Foo> CurrentCollection;
ICollection<Foo> DownloadedItems;
//LINQ: Add any downloaded items where the bar Foo.bar is not already in the collection?
You start by finding which elements are not already in the collection:
var newItems = DownloadedItems.Where(x => !CurrentCollection.Any(y => x.bar == y.bar));
And then just add them:
foreach(var item in newItems)
{
CurrentCollection.Add(item);
}
Note that the first operation may have quadratic complexity if the size of DownloadedItems
is close to the size of CurrentCollection
. If that ends up causing problems (measure first!), you can use a HashSet
to bring the complexity down to linear:
// collect all existing values of the property bar
var existingValues = new HashSet<Foo>(from x in CurrentCollection select x.bar);
// pick items that have a property bar that doesn't exist yet
var newItems = DownloadedItems.Where(x => !existingValues.Contains(x.bar));
// Add them
foreach(var item in newItems)
{
CurrentCollection.Add(item);
}
You can use Enumerable.Except:
It will compare the two lists and return elements that appear only in the first list.
CurrentCollection.AddRange(DownloadedItems.Except(CurrentCollection));
Using R.Martinho Fernandes method and converting to 1 line:
CurrentCollection.AddRange(DownloadedItems.Where(x => !CurrentCollection.Any(y => y.bar== x.bar)));
You can call the Any
method and pass a value to compare to whatever property of the type of object in the collection
if (!CurrentCollection.Any(f => f.bar == someValue))
{
// add item
}
a more complete solution could be:
DownloadedItems.Where(d => !CurrentCollection.Any(c => c.bar == d.bar)).ToList()
.ForEach(f => CurrentCollection.Add(f));
Or using All
CurrentCollection
.AddRange(DownloadedItems.Where(x => CurrentCollection.All(y => y.bar != x.bar)));
One thing that you can do also i think it is the easiest way is to une a HashSet instead of a List, by default the HashSet don't add redundant values.
List<int> current = new List<int> { 1, 2 };
List<int> add = new List<int> { 2, 3 };
current.AddRange(add.Except(current));
This will result in 1,2,3, using the default comparing.
This will also work for Foo
if you change the compare behaviour:
public class Foo : IEquatable<Foo>
{
public Int32 bar;
public bool Equals(Foo other)
{
return bar == other.bar;
}
public override bool Equals(object obj) => Equals(obj as Foo);
public override int GetHashCode() => (bar).GetHashCode(); // (prop1,prop2,prop3).GetHashCode()
}
You could also implement an IEqualityComparer<Foo>
, and pass it as second parameter to except
current.AddRange(add.Except(current, new FooComparer()));
public class FooComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
return x.bar.Equals(y.bar);
}
public int GetHashCode(Foo obj)
{
return obj.bar.GetHashCode();
}
}
internal static class ExtensionMethod
{
internal static ICollection<T> AddIfExists<T>(this ICollection<T> list, ICollection<T> range)
{
foreach (T item in range)
{
if (!list.Contains(item))
list.Add(item);
}
return list;
}
}
ICollection<Foo> CurrentCollection;
ICollection<Foo> DownloadedItems;
CurrentCollection.AddIfExists(DownloadedItems)....
var newItems = DownloadedItems.Where(i => !CurrentCollection.Any(c => c.Attr == i.Attr));
You could do it like this:
CurrentCollection.Any(x => x.bar == yourGivenValue)
精彩评论