Использование LINQ для объектов Intersect и Except для определенного свойства - PullRequest
6 голосов
/ 22 октября 2010

Когда у меня есть 2 List<string> объектов, тогда я могу использовать Intersect и Except на них напрямую, чтобы получить вывод IEnumerable<string>. Это достаточно просто, но что, если я хочу пересечение / разобщение на чем-то более сложном?

Пример, попытка получить коллекцию ClassA объектов, которая является результатом пересечения на ClassA объекта AStr1 и ClassB объекта BStr; :

public class ClassA {
    public string AStr1 { get; set; }
    public string AStr2 { get; set; }
    public int AInt { get; set; }
}
public class ClassB {
    public string BStr { get; set; }
    public int BInt { get; set; }
}
public class Whatever {
    public void xyz(List<ClassA> aObj, List<ClassB> bObj) {
        // *** this line is horribly incorrect ***
        IEnumberable<ClassA> result =
            aObj.Intersect(bObj).Where(a, b => a.AStr1 == b.BStr);
    }
}

Как я могу исправить отмеченную линию, чтобы достичь этого пересечения.

Ответы [ 3 ]

12 голосов
/ 22 октября 2010

MoreLINQ имеет ExceptBy. У него пока нет IntersectBy, но вы можете легко написать свою собственную реализацию и, возможно, впоследствии добавить ее в MoreLINQ:)

Вероятно, это будет выглядеть что-то примерно так (без проверки ошибок):

public static IEnumerable<TSource> IntersectBy<TSource, TKey>(
    this IEnumerable<TSource> first,
    IEnumerable<TSource> second,
    Func<TSource, TKey> keySelector,
    IEqualityComparer<TKey> keyComparer)
{
    HashSet<TKey> keys = new HashSet<TKey>(first.Select(keySelector),
                                           keyComparer);
    foreach (var element in second)
    {
        TKey key = keySelector(element);
        // Remove the key so we only yield once
        if (keys.Remove(key))
        {
            yield return element;
        }
    }
}

Если вы хотите выполнить пересечение двух совершенно разных типов, которые имеют общий тип свойства, вы можете создать более общий метод с тремя параметрами типа (один для first, один для second и один для общего типа ключа).

3 голосов
/ 22 октября 2010

x ∈ A ∩ B тогда и только тогда, когда x ∈ A и x ∈ B.

Таким образом, для каждого a в aObj вы можете проверить, входит ли a.AStr1 в наборBStr значения.

public void xyz(List<ClassA> aObj, List<ClassB> bObj)
{
    HashSet<string> bstr = new HashSet<string>(bObj.Select(b => b.BStr));
    IEnumerable<ClassA> result = aObj.Where(a => bstr.Contains(a.AStr1));
}
1 голос
/ 26 октября 2010

этот код:

    public IEnumerable<ClassA> xyz(List<ClassA> aObj, List<ClassB> bObj)
    {
        IEnumerable<string> bStrs = bObj.Select(b => b.BStr).Distinct();
        return aObj.Join(bStrs, a => a.AStr1, b => b, (a, b) => a);
    }

прошел следующий тест:

    [TestMethod]
    public void PropertyIntersectionBasedJoin()
    {
        List<ClassA> aObj = new List<ClassA>()
                                {
                                    new ClassA() { AStr1 = "a" }, 
                                    new ClassA() { AStr1 = "b" }, 
                                    new ClassA() { AStr1 = "c" }
                                };
        List<ClassB> bObj = new List<ClassB>()
                                {
                                    new ClassB() { BStr = "b" }, 
                                    new ClassB() { BStr = "b" }, 
                                    new ClassB() { BStr = "c" }, 
                                    new ClassB() { BStr = "d" }
                                };

        var result = xyz(aObj, bObj);

        Assert.AreEqual(2, result.Count());
        Assert.IsFalse(result.Any(a => a.AStr1 == "a"));
        Assert.IsTrue(result.Any(a => a.AStr1 == "b"));
        Assert.IsTrue(result.Any(a => a.AStr1 == "c"));
    }
...