Add items to a collection if the collection does NOT already contain it by comparing a property of the items?
Asked Answered
I

10

61

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
{
    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?
Iloilo answered 13/6, 2011 at 13:38 Comment(0)
A
72

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);
}
Actor answered 13/6, 2011 at 14:6 Comment(2)
Using the hash-map method made my method 5x faster.Barmecide
I think this is the best practiceDisseisin
A
25

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));
Averse answered 12/1, 2018 at 2:13 Comment(0)
P
16

Using R.Martinho Fernandes method and converting to 1 line:

CurrentCollection.AddRange(DownloadedItems.Where(x => !CurrentCollection.Any(y => y.bar== x.bar)));
Peptide answered 23/12, 2011 at 6:20 Comment(0)
D
5

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));
Deangelo answered 13/6, 2011 at 13:41 Comment(0)
H
2

Or using All

CurrentCollection
    .AddRange(DownloadedItems.Where(x => CurrentCollection.All(y => y.bar != x.bar)));
Harumscarum answered 20/9, 2017 at 11:24 Comment(0)
L
2

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.

Litigant answered 18/6, 2018 at 11:43 Comment(0)
D
1
    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();
        }
    }
Dogcart answered 28/5, 2020 at 15:55 Comment(0)
V
0
var newItems = DownloadedItems.Where(i => !CurrentCollection.Any(c => c.Attr == i.Attr));
Vish answered 13/6, 2011 at 13:42 Comment(0)
C
0

You could do it like this:

CurrentCollection.Any(x => x.bar == yourGivenValue)
Canteen answered 13/6, 2011 at 13:43 Comment(0)
K
0
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)....
Killjoy answered 26/2, 2021 at 16:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.