Linq gets values ​​not shared between multiple lists

What is the most efficient way to write a method that will compare n lists and return all values ​​that are not displayed in all lists, so

var lists = new List<List<int>> { new List<int> { 1, 2, 3, 4 }, new List<int> { 2, 3, 4, 5, 8 }, new List<int> { 2, 3, 4, 5, 9, 9 }, new List<int> { 2, 3, 3, 4, 9, 10 } }; public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists) { //...fast algorithm here } 

so that

lists.GetNonShared ();

returns 1, 5, 8, 9, 10

I had

 public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists) { return list.SelectMany(item => item) .Except(lists.Aggregate((a, b) => a.Intersect(b)); } 

But I was not sure that it was effective. The order does not matter. Thanks!

+8
c # linq array-algorithms
source share
4 answers
  public static IEnumerable<T> GetNonShared<T>(this IEnumerable<IEnumerable<T>> list) { return list.SelectMany(x => x.Distinct()).GroupBy(x => x).Where(g => g.Count() < list.Count()).Select(group => group.Key); } 
+5
source share

EDIT: I think I'll think of it this way:

You want to merge all lists, minus the intersection of all lists. This is effectively what your original does, leaving Except to perform the β€œset” Union operation, despite receiving duplicate inputs. In this case, I suspect that you could do it more efficiently by simply creating two HashSet and doing all the work in place:

 public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists) { using (var iterator = lists.GetEnumerator()) { if (!iterator.MoveNext()) { return new T[0]; // Empty } HashSet<T> union = new HashSet<T>(iterator.Current.ToList()); HashSet<T> intersection = new HashSet<T>(union); while (iterator.MoveNext()) { // This avoids iterating over it twice; it may not be necessary, // it depends on how you use it. List<T> list = iterator.Current.Toist(); union.UnionWith(list); intersection = intersection.IntersectWith(list); } union.ExceptWith(intersection); return union; } } 

Note that this is now impatient, not delayed.


Here's an alternative:

 public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists) { return list.SelectMany(list => list) .GroupBy(x => x) .Where(group => group.Count() < lists.Count) .Select(group => group.Key); } 

If the list can contain the same element more than once, you need a separate call:

 public IEnumerable<T> GetNonShared(this IEnumerable<IEnumerable<T>> lists) { return list.SelectMany(list => list.Distinct()) .GroupBy(x => x) .Where(group => group.Count() < list.Count) .Select(group => group.Key); } 

EDIT: now I fixed it, I understand your original code ... and I suspect I might find something better ... thinking ...

+2
source share

I think you need to create an intermediate step that will find all the elements that are common to all lists. This is easy to do with the established logic - this is just a set of elements in the first list, intersecting with a set of elements in each subsequent list. I do not think this step can be done in LINQ.

 class Program { static void Main(string[] args) { IEnumerable<IEnumerable<int>> lists = new List<IEnumerable<int>> { new List<int> { 1, 2, 3, 4 }, new List<int> { 2, 3, 4, 5, 8 }, new List<int> { 2, 3, 4, 5, 9, 9 }, new List<int> { 2, 3, 3, 4, 9, 10 } }; Console.WriteLine(string.Join(", ", GetNonShared(lists) .Distinct() .OrderBy(x => x) .Select(x => x.ToString()) .ToArray())); Console.ReadKey(); } public static HashSet<T> GetShared<T>(IEnumerable<IEnumerable<T>> lists) { HashSet<T> result = null; foreach (IEnumerable<T> list in lists) { result = (result == null) ? new HashSet<T>(list) : new HashSet<T>(result.Intersect(list)); } return result; } public static IEnumerable<T> GetNonShared<T>(IEnumerable<IEnumerable<T>> lists) { HashSet<T> shared = GetShared(lists); return lists.SelectMany(x => x).Where(x => !shared.Contains(x)); } } 
0
source share
 public static IEnumerable<T> GetNonShared<T>(this IEnumerable<IEnumerable<T>> list) { var lstCnt=list.Count(); //get the total number if items in the list return list.SelectMany (l => l.Distinct()) .GroupBy (l => l) .Select (l => new{n=l.Key, c=l.Count()}) .Where (l => lc<lstCnt) .Select (l => ln) .OrderBy (l => l) //can be commented ; } 

// use HashSet and SymmetricExceptWith for .net> = 4.5

0
source share

All Articles