How to find members that exist in at least two lists in a list of lists

I have a list of lists:

var stringLists = new List<string>[] { new List<string>(){ "a", "b", "c" }, new List<string>(){ "d", "b", "c" }, new List<string>(){ "a", "d", "c" } }; 

I want to extract all elements that are common in at least 2 lists. Therefore, for this example, I should get all the elements ["a", "b", "c", "d"] . I know how to find common elements for everyone, but I could not come up with any way to solve this problem.

+6
source share
4 answers

You can use something like this:

 var result = stringLists.SelectMany(l => l.Distinct()) .GroupBy(e => e) .Where(g => g.Count() >= 2) .Select(g => g.Key); 

Just for fun, some iterative solutions:

 var seen = new HashSet<string>(); var current = new HashSet<string>(); var result = new HashSet<string>(); foreach (var list in stringLists) { foreach(var element in list) if(current.Add(element) && !seen.Add(element)) result.Add(element); current.Clear(); } 

or:

 var already_seen = new Dictionary<string, bool>(); foreach(var list in stringLists) foreach(var element in list.Distinct()) already_seen[element] = already_seen.ContainsKey(element); var result = already_seen.Where(kvp => kvp.Value).Select(kvp => kvp.Key); 

or (inspired by Tim 's answer ):

 int tmp; var items = new Dictionary<string,int>(); foreach(var str in stringLists.SelectMany(l => l.Distinct())) { items.TryGetValue(str, out tmp); items[str] = tmp + 1; } var result = items.Where(kv => kv.Value >= 2).Select(kv => kv.Key); 
+9
source

You can use Dictionary<string, int> , the key is a string, and the value is count:

 Dictionary<string, int> itemCounts = new Dictionary<string,int>(); for(int i = 0; i < stringLists.Length; i++) { List<string> list = stringLists[i]; foreach(string str in list.Distinct()) { if(itemCounts.ContainsKey(str)) itemCounts[str] += 1; else itemCounts.Add(str, 1); } } var result = itemCounts.Where(kv => kv.Value >= 2); 

I am using list.Distinct() since you only want to count entries in different lists.

As requested, this is an extension method that can be reused with any type:

 public static IEnumerable<T> GetItemsWhichOccurAtLeastIn<T>(this IEnumerable<IEnumerable<T>> seq, int minCount, IEqualityComparer<T> comparer = null) { if (comparer == null) comparer = EqualityComparer<T>.Default; Dictionary<T, int> itemCounts = new Dictionary<T, int>(comparer); foreach (IEnumerable<T> subSeq in seq) { foreach (T x in subSeq.Distinct(comparer)) { if (itemCounts.ContainsKey(x)) itemCounts[x] += 1; else itemCounts.Add(x, 1); } } foreach(var kv in itemCounts.Where(kv => kv.Value >= minCount)) yield return kv.Key; } 

The use is simple:

 string result = String.Join(",", stringLists.GetItemsWhichOccurAtLeastIn(2)); // a,b,c,d 
+2
source

Follow these steps:

  • Create Dictionary Item -> Index List
  • cycle through all lists
  • for list number i: foreach element in the list: add me to the list in the dictionary at the position: dictionary[element].Add(i) (if it is not already present)
  • Indicate how many lists in the dictionary there are two entries
0
source

You can use SelectMany to flatten the list, and then select all items that meet two or more:

 var singleList = stringLists.SelectMany(p => p); var results = singleList.Where(p => singleList.Count(q => p == q) >= 2).Distinct(); 
-1
source

All Articles