Code
public static void Main()
{
List<int> list1 = new List<int> {1, 2, 3, 4, 5, 6 };
List<int> list2 = new List<int> {1, 2, 3 };
List<int> list3 = new List<int> {1, 2 };
var lists = new IEnumerable<int>[] { list1, list2, list3 };
var commons = GetCommonItems(lists);
Console.WriteLine("Common integers:");
foreach (var c in commons)
Console.WriteLine(c);
}
static IEnumerable<T> GetCommonItems<T>(IEnumerable<T>[] lists)
{
HashSet<T> hs = new HashSet<T>(lists.First());
for (int i = 1; i < lists.Length; i++)
hs.IntersectWith(lists[i]);
return hs;
}
As for the sample, I showed "list1" "list2" "list3", but I may have more than 50 lists that are generating each list using for each loop. How can I add programmatically each "list" to IEnumerable lists for comparing data of each list?
I tried many ways like conversion to list, Add, Append, Concat but nothing worked.
Is there any other best way to compare the N number of lists?
The output of Code: 1 2
You can create a list of lists and add lists to that list dynamically. Something like this:
var lists = new List<List<int>>();
lists.Add(new List<int> {1, 2, 3, 4, 5, 6 });
lists.Add(new List<int> {1, 2, 3 });
lists.Add(new List<int> {1, 2 });
foreach (var list in listSources)
lists.Add(list);
var commons = GetCommonItems(lists);
To find intersections you can use this solution for example: Intersection of multiple lists with IEnumerable.Intersect() (actually looks like that's what you are using already).
Also make sure to change the signature of the GetCommonItems method:
static IEnumerable<T> GetCommonItems<T>(List<List<T>> lists)
What you could do is allow the GetCommonItems method to accept a variable amount of parameters using the params keyword. This way, you avoid needing to create a new collection of lists.
It goes without saying, however, that if the amount of lists in your source is variable as well, this could be trickier to use.
I've also amended the GetCommonItems method to work like the code from https://stackoverflow.com/a/1676684/9945524
public static void Main()
{
List<int> list1 = new List<int> { 1, 2, 3, 4, 5, 6 };
List<int> list2 = new List<int> { 1, 2, 3 };
List<int> list3 = new List<int> { 1, 2 };
var commons = GetCommonItems(list1, list2, list3); // pass the lists here
Console.WriteLine("Common integers:");
foreach (var c in commons)
Console.WriteLine(c);
}
static IEnumerable<T> GetCommonItems<T>(params List<T>[] lists)
{
return lists.Skip(1).Aggregate(
new HashSet<T>(lists.First()),
(hs, lst) =>
{
hs.IntersectWith(lst);
return hs;
}
);
}
Alternate solution using your existing Main method.
EDIT: changed the type of lists to IEnumerable<IEnumerable<T>> as per comment in this answer.
public static void Main()
{
List<int> list1 = new List<int> { 1, 2, 3, 4, 5, 6 };
List<int> list2 = new List<int> { 1, 2, 3 };
List<int> list3 = new List<int> { 1, 2 };
var lists = new List<List<int>> { list1, list2, list3 };
var commons = GetCommonItems(lists);
Console.WriteLine("Common integers:");
foreach (var c in commons)
Console.WriteLine(c);
}
static IEnumerable<T> GetCommonItems<T>(IEnumerable<IEnumerable<T>> enumerables)
{
return enumerables.Skip(1).Aggregate(
new HashSet<T>(enumerables.First()),
(hs, lst) =>
{
hs.IntersectWith(lst);
return hs;
}
);
}
IEnumerable is immutable so you always should return an implementation of IEnumerable depending on your needs.
If I understand correctly you want to get common items of N lists. I would use LINQ for this.
My proposition:
1. make one list that contains all of the items. =>
var allElements = new List<int>();
var lists = new List<List<int>>();
foreach (list in lists)
allElements.AddRange(list);
Take items that are repetitive
allElements.GroupBy(x => x).Where(x => x.Count() > 1).Select(x => x).ToList();
Related
I have 2 list of ints and I need a list of all possible combinations without repetitions of 5 numbers. But it also needs to include all the ints from another list.
Example:
var takeFrom = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var mustInclude = new List<int> { 1, 3, 5 };
I have been using KwCombinatorics but it takes ages to finish. And almost 80% of the result is useless because it doesn't contain the ints from the mustInclude list.
Example of output:
var result = new List<int>
{
{ 1, 3, 5, 9, 10 },
{ 1, 3, 5, 8, 7 },
{ 1, 3, 5, 6, 9 },
}
It doesn't have to be in this order, as long as it doesn't contain repetitions.
Borrowing GetAllCombos from this Question, and using the idea from #juharr, I believe the following code gives you the results you are looking for.
List<int> takeFrom = new List<int> { 2, 4, 6, 7, 8, 9, 10 };
List<int> mustInclude = new List<int> { 1, 3, 5 };
protected void Page_Load(object sender, EventArgs e)
{
List<List<int>> FinalList = new List<List<int>>();
FinalList = GetAllCombos(takeFrom);
FinalList = AddListToEachList(FinalList, mustInclude);
gvCombos.DataSource = FinalList;
gvCombos.DataBind();
}
// Recursive
private static List<List<T>> GetAllCombos<T>(List<T> list)
{
List<List<T>> result = new List<List<T>>();
// head
result.Add(new List<T>());
result.Last().Add(list[0]);
if (list.Count == 1)
return result;
// tail
List<List<T>> tailCombos = GetAllCombos(list.Skip(1).ToList());
tailCombos.ForEach(combo =>
{
result.Add(new List<T>(combo));
combo.Add(list[0]);
result.Add(new List<T>(combo));
});
return result;
}
private static List<List<int>> AddListToEachList(List<List<int>> listOfLists, List<int> mustInclude)
{
List<List<int>> newListOfLists = new List<List<int>>();
//Go through each List
foreach (List<int> l in listOfLists)
{
List<int> newList = l.ToList();
//Add each item that should be in all lists
foreach(int i in mustInclude)
newList.Add(i);
newListOfLists.Add(newList);
}
return newListOfLists;
}
protected void gvCombos_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
List<int> drv = (List<int>)e.Row.DataItem;
Label lblCombo = (Label)e.Row.FindControl("lblCombo");
foreach (int i in drv)
lblCombo.Text += string.Format($"{i} ");
}
}
GetAllCombos gives you all the combinations without the numbers required by all Lists, and then the second AddListToEachList method will add the required numbers to each List.
As already suggested in the comments, you can remove the three required numbers from the list and generate the combinations of two instead of five.
Something like this:
takeFrom = takeFrom.Except(mustInclude).ToList();
listOfPairs = KwCombinatorics(takeFrom, 2);
result = listOfPairs.Select(pair => mustInclude.Concat(pair).ToList()).ToList();
Given a list of pairs such as
List<int> pair1 = new List<int>() { 1, 3};
List<int> pair2 = new List<int>() { 1, 2 };
List<int> pair3 = new List<int>() { 5, 3 };
List<int> pair4 = new List<int>() { 7, 8 };
List<int> pair5 = new List<int>() { 8, 11 };
List<int> pair6 = new List<int>() { 6, 9 };
List<int> pair7 = new List<int>() { 2, 13 };
List<int> pair8 = new List<int>() { 13, 16 };
How can I find all of the unions where the pairs intersect?
Output should be something like the following:
1,2,3,5,13,16
7,8,11
6,9
// create lists of pairs to sort through
static void links2XML(SQLiteConnection m_dbConnection)
{
List<int> pair1 = new List<int>() { 1, 3};
List<int> pair2 = new List<int>() { 1, 2 };
List<int> pair3 = new List<int>() { 5, 3 };
List<int> pair4 = new List<int>() { 7, 8 };
List<int> pair5 = new List<int>() { 8, 11 };
List<int> pair6 = new List<int>() { 6, 9 };
List<int> pair7 = new List<int>() { 2, 13 };
List<int> pair8 = new List<int>() { 13, 16 };
var pairs = new List<List<int>>();
pairs.Add(pair1);
pairs.Add(pair2);
pairs.Add(pair3);
pairs.Add(pair4);
pairs.Add(pair5);
pairs.Add(pair6);
pairs.Add(pair7);
pairs.Add(pair8);
var output = new List<int>();
foreach (var pair in pairs)
{
foreach (int i in followLinks(pair, pairs))
{
Console.Write(i + ",");
}
Console.WriteLine();
}
}
// loop through pairs to find intersections and recursively call function to
//build full list of all such ints
static List<int> followLinks(List<int> listA, List<List<int>> listB)
{
var links = listA;
var listC = listB.ToList();
bool added = false;
foreach (var l in listB)
{
var result = listA.Intersect(l);
if (result.Count<int>() > 0)
{
links = links.Union<int>(l).ToList();
listC.Remove(l); // remove pair for recursion after adding
added = true;
}
}
if (added)
{
followLinks(links, listC); //recursively call function with updated
//list of pairs and truncated list of lists of pairs
return links;
}
else return links;
}
Code should query the lists of pairs and output groups. I've tried this a few different ways, and this seems to have gotten the closest. I'm sure it requires a recursive loop, but figuring out the structure of it is just not making sense to me at the moment.
To clarify for some of the questions the number pairs are random that I chose for this question. The actual data set will be far larger and pulled from a database. That part is irrelevant to my question, though, and it's already solved anyway. It's really just this sorting that is giving me trouble.
To further clarify, the output will find a list of all of the integers from each pair that had an intersection... given pairs 1,2 and 1,3 the output would be 1,2,3. Given pairs 1,2 and 3,5, the output would be 1,2 for one list and 3,5 for the other. Hopefully that makes it clearer what I'm trying to find.
I used this function to return the full hash set of all links then loop through all of the initial pairs to feed this function. I filter the results to remove duplicates, and it solves the issue. Suggestion was from user Sven.
// Follow links to build hashset of all linked tags
static HashSet<int> followLinks(HashSet<int> testHs, List<HashSet<int>> pairs)
{
while (true)
{
var tester = new HashSet<int>(testHs);
bool keepGoing = false;
foreach (var p in pairs)
{
if (testHs.Overlaps(p))
{
testHs.UnionWith(p);
keepGoing = true;
}
}
for (int i = pairs.Count - 1; i == 0; i-- )
{
if (testHs.Overlaps(pairs[i]))
{
testHs.UnionWith(pairs[i]);
keepGoing = true;
}
}
if (!keepGoing) break;
if (testHs.SetEquals(tester)) break;
}
return testHs;
}
I'm quite new in using List as arrays in C#. So I've encounter a problem while using it.
I'm trying to removed an int[] (integer array) from a List<int[]> using the Remove but failed to removed the int[] from the List<int[]>.
here is the code:
List<int[]> trash = new List<int[]>()
{
new int[] {0,1},
new int[] {1,0},
new int[] {1,1}
};
int[] t1 = {0,1};
trash.Remove(t1);
Is it just a bug?
Or it doesn't recognize int[] ?
The problem is that every array type is a reference type and List removes items based on equality where equality for reference types is by default reference equality. That means, you have to remove the very same array as is in the list.
The following for example works perfectly well:
int[] t1 = {0,1};
List<int[]> trash = new List<int[]>()
{
t1,
new int[] {1,0},
new int[] {1,1}
};
trash.Remove(t1);
If you want to remove all the lists which have the same contents (in the same order) as a target list, you can do so using List.RemoveAll() along with Linq's SequenceEqual():
List<int[]> trash = new List<int[]>
{
new [] {0, 1},
new [] {1, 0},
new [] {1, 1}
};
int[] t1 = {0, 1};
trash.RemoveAll(element => element.SequenceEqual(t1));
Console.WriteLine(trash.Count); // Prints 2
This is very slow though. Better to use an index if you can.
Error is List of array uses reference type data. therefore please use the removeAt method of List like below:
List<int[]> trash = new List<int[]>()
{
new int[] {0,1},
new int[] {1,0},
new int[] {1,1}
};
trash.RemoveAt(0);
With RemoveAt you need to pass the index of integer array you want to remove from the list.
Your t1 variable is a new instance of the array. So it won't be equal to the first element in the list.
Try:
trash.Remove(trash[0]);
or
trash.RemoveAt(0);
The .Remove method looks the address of the element. If they are equal, then it removes. You should do like this.
int[] t1 = {0,1};
int[] t2 =new int[] {1,0};
int[] t3 =new int[] {1,1};
List<int[]> trash = new List<int[]>()
{
t1,t2,t3
};
trash.Remove(t1);
foreach(var x in trash)
{
if(x[0] == t1[0] && x[1] == t[1])
{
trash.Remove(x);
break;
}
}
this should work aswell
It is just because you are trying to remove item that is new.
Its address reference is different than the object that is already in list.That is why it is not remove.
Int is value type..
And Int[] is reference type..
So when you do it with Int list
List<int> trash = new List<int>(){ 1, 13, 5 };
int t1 = 13;
trash.Remove(t1);//it will removed
But for Int[]
List<int[]> trash = new List<int[]>()
{
new int[] {0,1},
new int[] {1,0},
new int[] {1,1}
};
var t1 = {0,1};
trash.Remove(t1);//t1 will not removed because "t1" address reference is different than the "new int[] {0,1}" item that is in list.
To remove-
trash.Remove(trash.Find(a => a.SequenceEqual(t1)));
SequenceEqual() Determines whether two sequences are equal by comparing the elements by using the default equality comparer for their type.
If you want to remove the exact sequence, but you don't have the possibility to remove the exact object (sequence coming out from elsewhere) you can search for the right sequence using a lambda expression or an anonymous method:
List<int[]> trash = new List<int[]>
{
new [] {0, 1},
new [] {1, 0},
new [] {1, 1}
};
int[] t1 = { 0, 1 };
//using anonymous method
trash.RemoveAll(delegate(int[] element) { return element.SequenceEqual(t1); });
//using lambda expression
trash.RemoveAll(element => element.SequenceEqual(t1));
I have two generic list :
List<string> TestList1 = new List<string>();
List<string> TestList2 = new List<string>();
TestList1.Add("1");
TestList1.Add("2");
TestList1.Add("3");
TestList2.Add("3");
TestList2.Add("4");
TestList2.Add("5");
What is the fastest way to find common items across these lists?
Assuming you use a version of .Net that has LINQ, you can use the Intersect extension method:
var CommonList = TestList1.Intersect(TestList2)
If you have lists of objects and want to get the common objects for some property then use;
var commons = TestList1.Select(s1 => s1.SomeProperty).ToList().Intersect(TestList2.Select(s2 => s2.SomeProperty).ToList()).ToList();
Note: SomeProperty refers to some criteria you want to implement.
Assuming you have LINQ available. I don't know if it's the fastest, but a clean way would be something like:
var distinctStrings = TestList1.Union(TestList2).Distinct();
var distinctStrings = TestList1.Union(TestList2);
Update: well never mind my answer, I've just learnt about Intersect as well!
According to an update in the comments, Unions apply a distinct, which makes sense now that I think about it.
You can do this by counting occurrences of all items in all lists - those items whose occurrence count is equal to the number of lists, are common to all lists:
static List<T> FindCommon<T>(IEnumerable<List<T>> lists)
{
Dictionary<T, int> map = new Dictionary<T, int>();
int listCount = 0; // number of lists
foreach (IEnumerable<T> list in lists)
{
listCount++;
foreach (T item in list)
{
// Item encountered, increment count
int currCount;
if (!map.TryGetValue(item, out currCount))
currCount = 0;
currCount++;
map[item] = currCount;
}
}
List<T> result= new List<T>();
foreach (KeyValuePair<T,int> kvp in map)
{
// Items whose occurrence count is equal to the number of lists are common to all the lists
if (kvp.Value == listCount)
result.Add(kvp.Key);
}
return result;
}
Sort both arrays and start from the top of both and compare if they are equal.
Using a hash is even faster: Put the first array in a hash, then compare every item of the second array if it is already in the hash.
I don't know those Intersect and Union are implemented. Try to find out their running time if you care about the performance. Of course they are better suited if you need clean code.
Use the Intersect method:
IEnumerable<string> result = TestList1.Intersect(TestList2);
Using HashSet for fast lookup. Here is the solution:
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
List<int> list1 = new List<int> {1, 2, 3, 4, 5, 6 };
List<int> list2 = new List<int> {1, 2, 3 };
List<int> list3 = new List<int> {1, 2 };
var lists = new IEnumerable<int>[] {list1, list2, list3 };
var commons = GetCommonItems(lists);
Console.WriteLine("Common integers:");
foreach (var c in commons)
Console.WriteLine(c);
}
static IEnumerable<T> GetCommonItems<T>(IEnumerable<T>[] lists)
{
HashSet<T> hs = new HashSet<T>(lists.First());
for (int i = 1; i < lists.Length; i++)
hs.IntersectWith(lists[i]);
return hs;
}
}
Following the lead of #logicnp on counting the number of lists containing each member, once you have your list of lists, it's pretty much one line of code:
List<int> l1, l2, l3, cmn;
List<List<int>> all;
l1 = new List<int>() { 1, 2, 3, 4, 5 };
l2 = new List<int>() { 1, 2, 3, 4 };
l3 = new List<int>() { 1, 2, 3 };
all = new List<List<int>>() { l1, l2, l3 };
cmn = all.SelectMany(x => x).Distinct()
.Where(x => all .Select(y => (y.Contains(x) ? 1 : 0))
.Sum() == all.Count).ToList();
Or, if you prefer:
public static List<T> FindCommon<T>(IEnumerable<List<T>> Lists)
{
return Lists.SelectMany(x => x).Distinct()
.Where(x => Lists.Select(y => (y.Contains(x) ? 1 : 0))
.Sum() == Lists.Count()).ToList();
}
I'm trying to find an intersect with LINQ.
Sample:
List<int> int1 = new List<int>() { 1,2 };
List<int> int2 = new List<int>();
List<int> int3 = new List<int>() { 1 };
List<int> int4 = new List<int>() { 1, 2 };
List<int> int5 = new List<int>() { 1 };
Want to return: 1 as it exists in all lists.. If I run:
var intResult= int1
.Intersect(int2)
.Intersect(int3)
.Intersect(int4)
.Intersect(int5).ToList();
It returns nothing as 1 obviously isn't in the int2 list. How do I get this to work regardless if one list is empty or not ?
Use the above example or:
List<int> int1 = new List<int>() { 1,2 };
List<int> int2 = new List<int>();
List<int> int3 = new List<int>();
List<int> int4 = new List<int>();
List<int> int5 = new List<int>();
How do I return 1 & 2 in this case.. I don't know ahead of time if the lists are populated...
If you need it in a single step, the simplest solution is to filter out empty lists:
public static IEnumerable<T> IntersectNonEmpty<T>(this IEnumerable<IEnumerable<T>> lists)
{
var nonEmptyLists = lists.Where(l => l.Any());
return nonEmptyLists.Aggregate((l1, l2) => l1.Intersect(l2));
}
You can then use it on a collection of lists or other IEnumerables:
IEnumerable<int>[] lists = new[] { l1, l2, l3, l4, l5 };
var intersect = lists.IntersectNonEmpty();
You may prefer a regular static method:
public static IEnumerable<T> IntersectNonEmpty<T>(params IEnumerable<T>[] lists)
{
return lists.IntersectNonEmpty();
}
var intersect = ListsExtensionMethods.IntersectNonEmpty(l1, l2, l3, l4, l5);
You could write an extension method to define that behaviour. Something like
static class MyExtensions
{
public static IEnumerable<T> IntersectAllIfEmpty<T>(this IEnumerable<T> list, IEnumerable<T> other)
{
if (other.Any())
return list.Intersect(other);
else
return list;
}
}
So the code below would print 1.
List<int> list1 = new List<int>() { 1, 2 };
List<int> list2 = new List<int>();
List<int> list3 = new List<int>() { 1 };
foreach (int i in list1.IntersectAllIfEmpty(list2).IntersectAllIfEmpty(list3))
Console.WriteLine(i);
Update:
Anon brings up a good point in the comments to the question. The above function will result in an empty set if list itself is empty, which should be desirable. This means if the first list in the method chain or the result set of any intersection is empty, the final result will be empty.
To allow for an empty first list but not for empty result sets, you could take a different approach. This is a method which is not an extension method, but rather takes a params array of IEnumerables and first filters out the empty sets and then attempts to intersect the rest.
public static IEnumerable<T> IntersectAllIfEmpty<T>(params IEnumerable<T>[] lists)
{
IEnumerable<T> results = null;
lists = lists.Where(l => l.Any()).ToArray();
if (lists.Length > 0)
{
results = lists[0];
for (int i = 1; i < lists.Length; i++)
results = results.Intersect(lists[i]);
}
else
{
results = new T[0];
}
return results;
}
You would use it like this
List<int> list0 = new List<int>();
List<int> list1 = new List<int>() { 1, 2 };
List<int> list2 = new List<int>() { 1 };
List<int> list3 = new List<int>() { 1,2,3 };
foreach (int i in IntersectAllIfEmpty(list0, list1, list2, list3))
{
Console.WriteLine(i);
}