LEFT OUTER JOIN in LINQ

后端 未结 22 2517
臣服心动
臣服心动 2020-11-21 04:49

How to perform left outer join in C# LINQ to objects without using join-on-equals-into clauses? Is there any way to do that with where clause? Corr

相关标签:
22条回答
  • Simple solution for the LEFT OUTER JOIN:

    var setA = context.SetA;
    var setB = context.SetB.Select(st=>st.Id).Distinct().ToList();
    var leftOuter  = setA.Where(stA=> !setB.Contains(stA.Id)); 
    

    notes:

    • To improve performance SetB could be converted to a Dictionary (if that is done then you have to change this: !setB.Contains(stA.Id)) or a HashSet
    • When there is more than one field involved this could be achieve using Set operations and a class that implement: IEqualityComparer
    0 讨论(0)
  • 2020-11-21 05:37

    Extension method that works like left join with Join syntax

    public static class LinQExtensions
    {
        public static IEnumerable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(
            this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, 
            Func<TOuter, TKey> outerKeySelector, 
            Func<TInner, TKey> innerKeySelector, 
            Func<TOuter, TInner, TResult> resultSelector)
        {
            return outer.GroupJoin(
                inner, 
                outerKeySelector, 
                innerKeySelector,
                (outerElement, innerElements) => resultSelector(outerElement, innerElements.FirstOrDefault()));
        }
    }
    

    just wrote it in .NET core and it seems to be working as expected.

    Small test:

            var Ids = new List<int> { 1, 2, 3, 4};
            var items = new List<Tuple<int, string>>
            {
                new Tuple<int, string>(1,"a"),
                new Tuple<int, string>(2,"b"),
                new Tuple<int, string>(4,"d"),
                new Tuple<int, string>(5,"e"),
            };
    
            var result = Ids.LeftJoin(
                items,
                id => id,
                item => item.Item1,
                (id, item) => item ?? new Tuple<int, string>(id, "not found"));
    
            result.ToList()
            Count = 4
            [0]: {(1, a)}
            [1]: {(2, b)}
            [2]: {(3, not found)}
            [3]: {(4, d)}
    
    0 讨论(0)
  • 2020-11-21 05:38

    An implementation of left outer join by extension methods could look like

    public static IEnumerable<Result> LeftJoin<TOuter, TInner, TKey, Result>(
      this IEnumerable<TOuter> outer, IEnumerable<TInner> inner
      , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
      , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
      {
        if (outer == null)
          throw new ArgumentException("outer");
    
        if (inner == null)
          throw new ArgumentException("inner");
    
        if (outerKeySelector == null)
          throw new ArgumentException("outerKeySelector");
    
        if (innerKeySelector == null)
          throw new ArgumentException("innerKeySelector");
    
        if (resultSelector == null)
          throw new ArgumentException("resultSelector");
    
        return LeftJoinImpl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer ?? EqualityComparer<TKey>.Default);
      }
    
      static IEnumerable<Result> LeftJoinImpl<TOuter, TInner, TKey, Result>(
          IEnumerable<TOuter> outer, IEnumerable<TInner> inner
          , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
          , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
      {
        var innerLookup = inner.ToLookup(innerKeySelector, comparer);
    
        foreach (var outerElment in outer)
        {
          var outerKey = outerKeySelector(outerElment);
          var innerElements = innerLookup[outerKey];
    
          if (innerElements.Any())
            foreach (var innerElement in innerElements)
              yield return resultSelector(outerElment, innerElement);
          else
            yield return resultSelector(outerElment, default(TInner));
         }
       }
    

    The resultselector then has to take care of the null elements. Fx.

       static void Main(string[] args)
       {
         var inner = new[] { Tuple.Create(1, "1"), Tuple.Create(2, "2"), Tuple.Create(3, "3") };
         var outer = new[] { Tuple.Create(1, "11"), Tuple.Create(2, "22") };
    
         var res = outer.LeftJoin(inner, item => item.Item1, item => item.Item1, (it1, it2) =>
         new { Key = it1.Item1, V1 = it1.Item2, V2 = it2 != null ? it2.Item2 : default(string) });
    
         foreach (var item in res)
           Console.WriteLine(string.Format("{0}, {1}, {2}", item.Key, item.V1, item.V2));
       }
    
    0 讨论(0)
  • 2020-11-21 05:38
    (from a in db.Assignments
         join b in db.Deliveryboys on a.AssignTo equals b.EmployeeId  
    
         //from d in eGroup.DefaultIfEmpty()
         join  c in  db.Deliveryboys on a.DeliverTo equals c.EmployeeId into eGroup2
         from e in eGroup2.DefaultIfEmpty()
         where (a.Collected == false)
         select new
         {
             OrderId = a.OrderId,
             DeliveryBoyID = a.AssignTo,
             AssignedBoyName = b.Name,
             Assigndate = a.Assigndate,
             Collected = a.Collected,
             CollectedDate = a.CollectedDate,
             CollectionBagNo = a.CollectionBagNo,
             DeliverTo = e == null ? "Null" : e.Name,
             DeliverDate = a.DeliverDate,
             DeliverBagNo = a.DeliverBagNo,
             Delivered = a.Delivered
    
         });
    
    0 讨论(0)
提交回复
热议问题