3

I have collection of elements and one additional small collection as filter.

I need to separate it on 2 new collections by some filter. In my case it is first collection that contains some elements and another that doesn't.

There aren't items that doesn't exists out of that 2 new collections.

I did it like :

var collection1= baseCollection.Where(r => filterCollection.Contains(r.Property)).ToList();
var collection2= baseCollection.Where(r => !filterCollection.Contains(r.Property)).ToList();

But is there another, I hope more elegant way, to separate collection? For me it looks like "I repeat myself", use almost the same code 2 times.

Gilad Green
  • 36,708
  • 7
  • 61
  • 95
demo
  • 6,038
  • 19
  • 75
  • 149

1 Answers1

8
  1. You can create a variable for the function - this way you will not "repeat yourself" (wouldn't use in this case, there are better options below, but still an option):

    Func<YourClass,bool> filtering = (r) => filterCollection.Contains(r.Property);
    
    var collection1 = baseCollection.Where(r => filtering(r));
    var collection2 = baseCollection.Where(r => !filtering(r));
    
  2. If your type of the collection overrides Equals and GetHashCode you can use Except:

    var collection1 = baseCollection.Where(r => filterCollection.Contains(r.Property));
    var collection2 = baseCollection.Except(collection1);
    
  3. Using Except with a given IEqualityComparer (Check also first comment for guidlines):

    public class Comparer : IEqualityComparer<YourClass>
    {
        public bool Equals(YourClass x, YourClass y)
        {
            // Your implementation
        }
    
        public int GetHashCode(YourClass obj)
        {
            // Your implementation
        }
    }
    
    var collection1 = baseCollection.Where(r => filterCollection.Contains(r.Property));
    var collection2 = baseCollection.Except(collection1, new Comparer());
    
  4. You can also use GroupBy (probably less good performance wise):

    var result baseCollection.GroupBy(r => filterCollection.Contains(r.Property))
                             .ToDictionary(key => key.Key, value => value.ToList());
    
    var collection1 = result[true];
    var collection2 = result[false];
    
  5. Otherwise another way will just to use a loop:

    List<YourType> collection1 = new List<YourType>();
    List<YourType> collection2 = new List<YourType>();
    foreach(var item in baseCollection)
    {
        if(filterCollection.Contains(item.Property))
        {
            collection1.Add(item);
        }
        else
        {
            collection2.Add(item);
        }
    }
    
Graham
  • 7,431
  • 18
  • 59
  • 84
Gilad Green
  • 36,708
  • 7
  • 61
  • 95
  • Is it faster than 2 times `Where` ? – demo Aug 26 '16 at 11:11
  • 1
    @demo - about performance I'm not sure. One must check. Probably the fastest is option 3 - *o(n) for the baseCollection part (not including the contains because all solutions use it)*. – Gilad Green Aug 26 '16 at 11:18
  • 2
    `Except` is faster than `Where` since it's using a set based approach to filter. Instead of overriding `Equals` + `GetHashCode` you could also use the [overload](https://msdn.microsoft.com/en-us/library/bb336390(v=vs.110).aspx) that takes an `IEqualityComparer`. – Tim Schmelter Aug 26 '16 at 11:19