Find all intersecting data, not just the unique values

前端 未结 4 431
孤城傲影
孤城傲影 2021-01-11 13:52

I thought that I understood Intersect, but it turns out I was wrong.

 List list1 = new List() { 1, 2, 3, 2, 3};
 List<         


        
相关标签:
4条回答
  • 2021-01-11 14:15
    var set = new HashSet(list1.Intersect(list2));
    return list1.Concat(list2).Where(i=>set.Contains(i));
    
    0 讨论(0)
  • 2021-01-11 14:17

    I don't believe this is possible with the built-in APIs. But you could use the following to get the result you're looking for.

    IEnumerable<T> Intersect2<T>(this IEnumerable<T> left, IEnumerable<T> right) {
      var map = left.ToDictionary(x => x, y => false);
      foreach ( var item in right ) {
        if (map.ContainsKey(item) ) {
          map[item] = true;
        }
      }
      foreach ( var cur in left.Concat(right) ) {
        if ( map.ContainsKey(cur) ) {
          yield return cur;
        }
      }
    }
    
    0 讨论(0)
  • 2021-01-11 14:18

    Maybe this could help: https://gist.github.com/mladenb/b76bcbc4063f138289243fb06d099dda

    The original Except/Intersect return a collection of unique items, even though their contract doesn't state so (e.g. the return value of those methods isn't a HashSet/Set, but rather IEnumerable), which is probably a result of a poor design decision. Instead, we can use more intuitive implementation, which returns as much of the same elements from the first enumeration as there are, not just a unique one (using Set.Contains).

    Further more, mapping function was added in order to help intersect/except collections of different types.

    If you don't need to intersect/except collections of different types, just inspect the source code of the Intersect/Except and change the part which iterates through the first enumeration to use Set.Contains instead of Set.Add/Set.Remove.

    0 讨论(0)
  • 2021-01-11 14:20

    Let's see if we can precisely characterize what you want. Correct me if I am wrong. You want: all elements of list 1, in order, that also appear in list 2, followed by all elements of list 2, in order, that also appear in list 1. Yes?

    Seems straightforward.

    return list1.Where(x=>list2.Contains(x))
         .Concat(list2.Where(y=>list1.Contains(y)))
         .ToList();
    

    Note that this is not efficient for large lists. If the lists have a thousand items each then this does a couple million comparisons. If you're in that situation then you want to use a more efficient data structure for testing membership:

    list1set = new HashSet(list1);
    list2set = new HashSet(list2);
    
    return list1.Where(x=>list2set.Contains(x))
         .Concat(list2.Where(y=>list1set.Contains(y)))
         .ToList();
    

    which only does a couple thousand comparisons, but potentially uses more memory.

    0 讨论(0)
提交回复
热议问题