got this little problem in my little C# hobby project that I can't quite work out. I have been stuck in lots of messy and complicated nested loops. Hope someone can give light.
I have a list of list of int, i.e. List<List<int>> . Assume each list of int contains unique items. The minimum size of the list is 5. I need to find exactly two lists of int (List A and List B) that share exactly three common items and another list of int (List X) that contains exactly one of these common items. Another condition must hold: none of the other lists contain any of these three items.
For example:
List<List<int>> allLists = new List<List<int>>();
allLists.Add(new List<int>() {1, 2, 3, 4});
allLists.Add(new List<int>() {1, 2});
allLists.Add(new List<int>() {3, 4});
allLists.Add(new List<int>() {3, 4, 5, 6, 7, 8, 9});
allLists.Add(new List<int>() {4, 6, 8});
allLists.Add(new List<int>() {5, 7, 9, 11});
allLists.Add(new List<int>() {6, 7, 8});
For the above example, I would hope to find a solution as:
ListA and ListB: [3, 5] // indices of allLists
ListX: 6 // index of allLists
The three shared items: [5, 7, 9]
The matching item in ListX: 7
Note: Depending on the content of lists, there may be multiple solutions. There may be also situations that no lists is found matching the above conditions.
I was stuck in some messy nested loops. I was thinking if anyone may come up with a simple and efficient solution (possibly with LINQ?)
Originally I had something stupid like the following:
for (var i = 0; i < allLists.Count - 1; i++)
{
if (allLists[i].Count > 2)
{
for (var j = i + 1; j < allLists.Count; j++)
{
List<int> sharedItems = allLists[i].Intersect(allLists[j]).ToList();
if (sharedItems.Count == 3)
{
foreach (var item in sharedItems)
{
int itemCount = 0;
int? possibleListXIndex = null;
for (var k = 0; k < allLists.Count; k++)
{
if (k != i && k != j && allLists[k].Contains(item))
{
// nested loops getting very ugly here... also not sure what to do....
}
}
}
}
}
}
}
Extended Problem
There is an extended version of this problem in my project. It is in the same fashion:
find exactly three lists of int (List A, List B and List C) that share exactly four common items
find another list of int (List X) that contains exactly one of the above common items
none of the other lists contain these four items.
I was thinking the original algorithm may become scalable to also cover the extended version without having to write another version of algorithm from scratch. With my nested loops above, I think I will have no choice but to add at least two deeper-level loops to cover four items and three lists.
I thank everyone for your contributions in advance! Truly appreciated.
Here's a solution that comes up with your answer. I wouldn't exactly called it efficient, but it's pretty simple to follow.
It breaks the work in two step. First it constructs a list of initial candidates where they have exactly three matches. The second step adds the ListX property and checks to see if the remaining criteria is met.
var matches = allLists.Take(allLists.Count - 1)
.SelectMany((x, xIdx) => allLists
.Skip(xIdx + 1)
.Select(y => new { ListA = x, ListB = y, Shared = x.Intersect(y) })
.Where(y => y.Shared.Count() == 3))
.SelectMany(x => allLists
.Where(y => y != x.ListA && y != x.ListB)
.Select(y => new
{
x.ListA,
x.ListB,
x.Shared,
ListX = y,
SingleShared = x.Shared.Intersect(y)
})
.Where(y => y.SingleShared.Count() == 1
&& !allLists.Any(z => z != y.ListA
&& z != y.ListB
&& z != y.ListX
&& z.Intersect(y.Shared).Any())));
You get the output below after running the following code.
ListA. 3: [3, 4, 5, 6, 7, 8, 9] ListB. 5: [5, 7, 9, 11] => [5, 7, 9], ListX. 6:[6, 7, 10] => 7
matches.ToList().ForEach(x => {
Console.WriteLine("ListA. {0}: [{1}] ListB. {2}: [{3}] => [{4}], ListX. {5}:[{6}] => {7}",
allLists.IndexOf(x.ListA),
string.Join(", ", x.ListA),
allLists.IndexOf(x.ListB),
string.Join(", ", x.ListB),
string.Join(", ", x.Shared),
allLists.IndexOf(x.ListX),
string.Join(", ", x.ListX),
string.Join(", ", x.SingleShared));
I will leave the exercise of further work such as which list matches which other one given your fairly generic requirement. So here, I find those that match 3 other values in a given array, processing all arrays - so there are duplicates here where an A matches a B and a B matches an A for example.
This should give you something you can work from:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
var s = new List<int>();
List<List<int>> allLists = new List<List<int>>();
allLists.Add(new List<int>()
{1, 2, 3, 4});
allLists.Add(new List<int>()
{1, 2});
allLists.Add(new List<int>()
{3, 4});
allLists.Add(new List<int>()
{3, 4, 5, 6, 7, 8, 9});
allLists.Add(new List<int>()
{4, 6, 8});
allLists.Add(new List<int>()
{5, 7, 9, 11});
allLists.Add(new List<int>()
{6, 7, 8});
/*
// To iterate over it.
foreach (List<int> subList in allLists)
{
foreach (int item in subList)
{
Console.WriteLine(item);
}
}
*/
var countMatch = 3;
/* iterate over our lists */
foreach (var sub in allLists)
{
/* not the sub list */
var ns = allLists.Where(g => g != sub);
Console.WriteLine("Check:{0}", ns.Count()); // 6 of the 7 lists so 6 to check against
//foreach (var glist in ns) - all of them, now refactor to filter them:
foreach (var glist in ns.Where(n=> n.Intersect(sub).Count() == countMatch))
{
var r = sub.Intersect(glist); // get all the matches of glist and sub
Console.WriteLine("Matches:{0} in {1}", r.Count(), glist.Count());
foreach (int item in r)
{
Console.WriteLine(item);
}
}
}
}
}
This will output this:
Hello World
Check:6
Check:6
Check:6
Check:6
Matches:3 in 3
4
6
8
Matches:3 in 4
5
7
9
Matches:3 in 3
6
7
8
Check:6
Matches:3 in 7
4
6
8
Check:6
Matches:3 in 7
5
7
9
Check:6
Matches:3 in 7
6
7
8
I think it would be better to break this functionality into several methods, then it will look easier to read.
var allLists = new List<List<int>>();
allLists.Add(new List<int>() {1, 2, 3, 4});
allLists.Add(new List<int>() {1, 2});
allLists.Add(new List<int>() {3, 4});
allLists.Add(new List<int>() {3, 4, 5, 6, 7, 8, 9});
allLists.Add(new List<int>() {4, 6, 8});
allLists.Add(new List<int>() {5, 7, 9, 11});
allLists.Add(new List<int>() {6, 7, 8});
var count = allLists.Count;
for (var i = 0; i < count - 1; i++)
{
var left = allLists[i];
if (left.Count > 2)
{
for (var j = i + 1; j < count; j++)
{
var right = allLists[j];
var sharedItems = left.Intersect(right).ToList();
if (sharedItems.Count == 3)
{
for (int k = 0; k < count; k++)
{
if (k == i || k == j)
continue;
var intersected = allLists[k].Intersect(sharedItems).ToList();
if (intersected.Count == 1)
{
Console.WriteLine($"Found index k:{k},i:{i},j:{j}, Intersected numbers:{string.Join(",",intersected)}");
}
}
}
}
}
}
I just thought if I try to find the item in List X first, I might need fewer loops in practice. Correct me if I am wrong.
public static void match(List<List<int>> allLists, int numberOfMainListsToExistIn, int numberOfCommonItems)
{
var possibilitiesToCheck = allLists.SelectMany(i => i).GroupBy(e => e).Where(e => (e.Count() == numberOfMainListsToExistIn + 1));
foreach (var pGroup in possibilitiesToCheck)
{
int p = pGroup.Key;
List<int> matchingListIndices = allLists.Select((l, i) => l.Contains(p) ? i : -1).Where(i => i > -1).ToList();
for (int i = 0; i < matchingListIndices.Count; i++)
{
int aIndex = matchingListIndices[i];
int bIndex = matchingListIndices[(i + 1) % matchingListIndices.Count];
int indexOfListXIndex = (i - 1 + matchingListIndices.Count) % matchingListIndices.Count;
int xIndex = matchingListIndices[indexOfListXIndex];
IEnumerable<int> shared = allLists[aIndex].Intersect(allLists[bIndex]).OrderBy(e => e);
IEnumerable<int> xSingle = shared.Intersect(allLists[xIndex]);
bool conditionsHold = false;
if (shared.Count() == numberOfCommonItems && xSingle.Count() == 1 && xSingle.Contains(p))
{
conditionsHold = true;
for (int j = 2; j < matchingListIndices.Count - 1; j++)
{
int cIndex = matchingListIndices[(i + j) % matchingListIndices.Count];
if (!Enumerable.SequenceEqual(shared, allLists[aIndex].Intersect(allLists[cIndex]).OrderBy(e => e)))
{
conditionsHold = false;
break;
}
}
if (conditionsHold)
{
List<int> theOtherListIndices = Enumerable.Range(0, allLists.Count - 1).Except(matchingListIndices).ToList();
if (theOtherListIndices.Any(x => shared.Intersect(allLists[x]).Count() > 0))
{
conditionsHold = false;
}
}
}
if (conditionsHold)
{
matchingListIndices.RemoveAt(indexOfListXIndex);
Console.Write("List A and B: {0}. ", String.Join(", ", matchingListIndices));
Console.Write("Common items: {0}. ", String.Join(", ", shared));
Console.Write("List X: {0}.", xIndex);
Console.WriteLine("Common item in list X: {0}. ", p);
}
}
}
}
For the above example, I will just call the method like this:
match(allLists, 2, 3);
This method will also work with the extended problem:
match(allLists, 3, 4);
... and even more if the problem is even further more extended to (4, 5) and so on...
Related
I have two C# Lists of different sizes e.g.
List<int> list1 = new List<int>{1,2,3,4,5,6,7};
List<int> list2 = new List<int>{4,5,6,7,8,9};
I want to use the linq Zip method to combine these two into a list of tuples that is of the size list1. Here is the resulting list I am looking for
{(1,4), (2,5), (3,6), (4,7), (5,8), (6,9), (7,0)} //this is of type List<(int,int)
Since the last item of list1 does not has a counterpart in list2, I fill up my last item of the resulting list with a default value (in this case 0 as in my case it will never appear in any of the original lists).
Is there a way I can use the linq Zip method alone to achieve this?
You can use Concat to make them both the same size, and then zip it:
var zipped = list1.Concat(Enumerable.Repeat(0,Math.Max(list2.Count-list1.Count,0)))
.Zip(list2.Concat(Enumerable.Repeat(0,Math.Max(list1.Count-list2.Count,0))),
(a,b)=>(a,b));
Or create an extension method:
public static class ZipExtension{
public static IEnumerable<TResult> Zip<TFirst,TSecond,TResult>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst,TSecond,TResult> func,
TFirst padder1,
TSecond padder2)
{
var firstExp = first.Concat(
Enumerable.Repeat(
padder1,
Math.Max(second.Count()-first.Count(),0)
)
);
var secExp = second.Concat(
Enumerable.Repeat(
padder2,
Math.Max(first.Count()-second.Count(),0)
)
);
return firstExp.Zip(secExp, (a,b) => func(a,b));
}
}
So you can use like this:
//last 2 arguments are the padder values for list1 and list2
var zipped = list1.Zip(list2, (a,b) => (a,b), 0, 0);
There is a useful and popular MoreLinq library. Install it and use.
using MoreLinq;
var result = list1.ZipLongest(list2, (x, y) => (x, y));
Try this using Zip function-
static void Main(string[] args)
{
List<int> firstList = new List<int>() { 1, 2, 3, 4, 5, 6, 0, 34, 56, 23 };
List<int> secondList = new List<int>() { 4, 5, 6, 7, 8, 9, 1 };
int a = firstList.Count;
int b = secondList.Count;
for (int k = 0; k < (a - b); k++)
{
if(a>b)
secondList.Add(0);
else
firstList.Add(0);
}
var zipArray = firstList.Zip(secondList, (c, d) => c + " " + d);
foreach(var item in zipArray)
{
Console.WriteLine(item);
}
Console.Read();
}
Or you can try this using ZipLongest Function by installing MoreLinq nuget package-
static void Main(string[] args)
{
List<int> firstList = new List<int>() { 1, 2, 3, 4, 5, 6, 0, 34, 56, 23 };
List<int> secondList = new List<int>() { 4, 5, 6, 7, 8, 9, 1 };
var zipArray = firstList.ZipLongest(secondList, (c, d) => (c,d));
foreach (var item in zipArray)
{
Console.WriteLine(item);
}
Console.Read();
}
Try this code-
static void Main(string[] args)
{
List<int> firstList=new List<int>() { 1, 2, 3, 4, 5, 6,0,34,56,23};
List<int> secondList=new List<int>() { 4, 5, 6, 7, 8, 9,1};
int a = firstList.Count;
int b = secondList.Count;
if (a > b)
{
for(int k=0;k<(a-b);k++)
secondList.Add(0);
}
else
{
for (int k = 0; k < (b-a); k++)
firstList.Add(0);
}
for(int i=0;i<firstList.Count;i++)
{
for(int j=0;j<=secondList.Count;j++)
{
if(i==j)
Console.Write($"({Convert.ToInt32(firstList[i])},{ Convert.ToInt32(secondList[j])})" + "");
}
}
Console.Read();
}
I want to find the index of sublist in list route which contains certain value(i), but I don't want to make a class of it.
Here is my code:
var route = new List<List<int>>();
for (int i = 0; i<DC1; ++i)
{
for (int j = 0; j<DC1; ++j)
{
if (routeopt.x[0, j] == 1)
{
List<int> subroute = new List<int>();
if (routeopt.x[i, j] == 1)
{
subroute.Add(j);
route.Add(subroute);
}
}
}
if(i == 0) continue;
for (int j = 1; j<DC1;j++ )
{
if (routeopt.x[i, j] == 1)
route[route.IndexOf(i)].Add ( j);
}
}
foreach (var subroute in route)
{
Console.Write("subroute: ");
foreach (int s in subroute)
Console.Write(s + " ");
Console.WriteLine();
}
For example, based on this code:
for (int j = 1; j < DC1;j++ )
{
if (routeopt.x[i, j] == 1)
route[route.IndexOf(i)].Add(j);
}
I want to make if x[1,3] == 1 then I can add 3 to sublist which contains 1.
this code route.IndexOf(i) is still get red underline, please help how to correct it. Thanks
You can use LINQ's Single method to retrieve the specific list you want given a predicate Contains(i). Here I am looking for the list that contains 6, and adds 7 to it.
var routes = new List<List<int>>()
{
new List<int>() {1, 2, 3},
new List<int>() {4, 5, 6},
};
List<int> targetList = routes.Single(i => i.Contains(6));
targetList.Add(7);
To get specifically the index of that list, then you can use IndexOf method like:
int targetListIndex = routes.IndexOf(targetList); // 1 in this example
Let's start from an example (which we turn into a test later):
List<List<int>> route = new List<List<int>>() {
new List<int>() {1, 2, 3, 4, 5}, // sublist #0: contains value == 4
new List<int>() {7, 8, 2, 9}, // sublist #1: doesn't contain value == 4
new List<int>() {9, 10, 4}, // sublist #2: contains value == 4
};
and we are looking for value within each of sublists
int value = 4;
finally, we want to have sublist indexes as the outcome: 0 and 2.
If it's your case, I suggest using Linq
List<List<int>> route = new List<List<int>>() {
new List<int>() {1, 2, 3, 4, 5},
new List<int>() {7, 8, 2, 9},
new List<int>() {9, 10, 4},
};
int value = 4;
var indexesFound = route
.Select((sublist, index) => new { // eh, new class, but anonymous one
sublist = sublist,
index = index, })
.Where(chunk => chunk.sublist.Contains(value))
.Select(chunk => chunk.index)
.ToArray(); // if you want, say, array materialization
Test
Console.Wrire(string.Join(", ", indexesFound));
Outcome:
0, 2
Edit: if you want to have just one index, you have to specify which one, e.g. for the very first index put .First() instead of .ToArray():
int firstIndexFound = route
.Select((sublist, index) => new { // eh, new class, but anonymous one
sublist = sublist,
index = index, })
.Where(chunk => chunk.sublist.Contains(value))
.Select(chunk => chunk.index)
.First(); // or .Last()
Suppose I have a sorted array such as
int[] sarr = new int[] { 0, 1, 3, 5 };
and I want to iterate through all combinations of size K in ascending order of their sum. For example, if K=2 then the combinations in order are
{0, 1} (sum = 1)
{1, 0} (sum = 1)
{0, 3} (sum = 3)
{3, 0} (sum = 3)
{3, 1} (sum = 4)
{1, 3} (sum = 4)
{5, 0} (sum = 5)
.
.
.
I want to do this without first getting all combinations because I want to stop as soon as I've found one that satisfies a condition Func<int[],bool> cond.
Is there a known way of doing this?
I would use yield return to describe all combinations, arrangements or whatever subcollections you want to generate and then use FirstOrDefault on the result.
That way you will only generate all subcollections if there isn't one you are looking for or the one you are looking for is the very last.
On about getting them ascending by the sum of the elements, sort the initial collection and then pick k elements from start to end. You can even generate combinations and from these to generate all possible permutations and thus you will get all your arrangements.
A quick way to get all combinations:
class Program
{
static void Main(string[] args)
{
var initialArray = new[] { 0, 1, 3, 5 };
var subArrayLength = 2;
foreach (var subArray in GetSubArrays(initialArray, subArrayLength))
Console.WriteLine($"[{string.Join(", ", subArray)}]");
Console.WriteLine("Searching for array that contains both 1 and 5.");
var arrayFulfillingCriteria = GetSubArrays(initialArray, subArrayLength).FirstOrDefault(array => array.Contains(1) && array.Contains(5));
if (arrayFulfillingCriteria != null)
Console.WriteLine($"[{string.Join(", ", arrayFulfillingCriteria)}]");
else
Console.WriteLine("No array found.");
}
static IEnumerable<int[]> GetSubArrays(int[] initialArray, int subArrayLength)
{
var indexStack = new Stack<int>(Enumerable.Range(0, subArrayLength));
do
{
var subArray = indexStack.Select(i => initialArray[i]).Reverse().ToArray();
yield return subArray;
var index = indexStack.Pop();
while (indexStack.Count != 0 && indexStack.Count < subArrayLength && index == initialArray.Length - (subArrayLength - indexStack.Count))
index = indexStack.Pop();
while (indexStack.Count < subArrayLength && index < initialArray.Length - (subArrayLength - indexStack.Count))
{
index++;
indexStack.Push(index);
}
}
while (indexStack.Count != 0);
}
}
The only reason I can think of where you would need arrangements (seeing as you order by sum) is that the items within the sub array need to be in a particular order.
Does this work for you?
Func<IEnumerable<int>, IEnumerable<IEnumerable<int>>> getAllSubsets = null;
getAllSubsets = xs =>
(xs == null || !xs.Any())
? Enumerable.Empty<IEnumerable<int>>()
: xs.Skip(1).Any()
? getAllSubsets(xs.Skip(1))
.SelectMany(ys => new [] { ys, xs.Take(1).Concat(ys) })
: new [] { Enumerable.Empty<int>(), xs.Take(1) };
Now, given this:
Func<int[],bool> cond = xs => true;
int[] sarr = new int[] { 0, 1, 3, 5, };
var result =
getAllSubsets(sarr)
.Where(xs => xs.Count() == 2)
.Where(xs => cond(xs.ToArray()));
I get this as the result:
{0, 1}
{0, 3}
{1, 3}
{0, 5}
{1, 5}
{3, 5}
How can I find the set of items that occur in 2 or more sequences in a sequence of sequences?
In other words, I want the distinct values that occur in at least 2 of the passed in sequences.
Note:
This is not the intersect of all sequences but rather, the union of the intersect of all pairs of sequences.
Note 2:
The does not include the pair, or 2 combination, of a sequence with itself. That would be silly.
I have made an attempt myself,
public static IEnumerable<T> UnionOfIntersects<T>(
this IEnumerable<IEnumerable<T>> source)
{
var pairs =
from s1 in source
from s2 in source
select new { s1 , s2 };
var intersects = pairs
.Where(p => p.s1 != p.s2)
.Select(p => p.s1.Intersect(p.s2));
return intersects.SelectMany(i => i).Distinct();
}
but I'm concerned that this might be sub-optimal, I think it includes intersects of pair A, B and pair B, A which seems inefficient. I also think there might be a more efficient way to compound the sets as they are iterated.
I include some example input and output below:
{ { 1, 1, 2, 3, 4, 5, 7 }, { 5, 6, 7 }, { 2, 6, 7, 9 } , { 4 } }
returns
{ 2, 4, 5, 6, 7 }
and
{ { 1, 2, 3} } or { {} } or { }
returns
{ }
I'm looking for the best combination of readability and potential performance.
EDIT
I've performed some initial testing of the current answers, my code is here. Output below.
Original valid:True
DoomerOneLine valid:True
DoomerSqlLike valid:True
Svinja valid:True
Adricadar valid:True
Schmelter valid:True
Original 100000 iterations in 82ms
DoomerOneLine 100000 iterations in 58ms
DoomerSqlLike 100000 iterations in 82ms
Svinja 100000 iterations in 1039ms
Adricadar 100000 iterations in 879ms
Schmelter 100000 iterations in 9ms
At the moment, it looks as if Tim Schmelter's answer performs better by at least an order of magnitude.
// init sequences
var sequences = new int[][]
{
new int[] { 1, 2, 3, 4, 5, 7 },
new int[] { 5, 6, 7 },
new int[] { 2, 6, 7, 9 },
new int[] { 4 }
};
One-line way:
var result = sequences
.SelectMany(e => e.Distinct())
.GroupBy(e => e)
.Where(e => e.Count() > 1)
.Select(e => e.Key);
// result is { 2 4 5 7 6 }
Sql-like way (with ordering):
var result = (
from e in sequences.SelectMany(e => e.Distinct())
group e by e into g
where g.Count() > 1
orderby g.Key
select g.Key);
// result is { 2 4 5 6 7 }
May be fastest code (but not readable), complexity O(N):
var dic = new Dictionary<int, int>();
var subHash = new HashSet<int>();
int length = array.Length;
for (int i = 0; i < length; i++)
{
subHash.Clear();
int subLength = array[i].Length;
for (int j = 0; j < subLength; j++)
{
int n = array[i][j];
if (!subHash.Contains(n))
{
int counter;
if (dic.TryGetValue(n, out counter))
{
// duplicate
dic[n] = counter + 1;
}
else
{
// first occurance
dic[n] = 1;
}
}
else
{
// exclude duplucate in sub array
subHash.Add(n);
}
}
}
This should be very close to optimal - how "readable" it is depends on your taste. In my opinion it is also the most readable solution.
var seenElements = new HashSet<T>();
var repeatedElements = new HashSet<T>();
foreach (var list in source)
{
foreach (var element in list.Distinct())
{
if (seenElements.Contains(element))
{
repeatedElements.Add(element);
}
else
{
seenElements.Add(element);
}
}
}
return repeatedElements;
You can skip already Intesected sequences, this way will be a little faster.
public static IEnumerable<T> UnionOfIntersects<T>(this IEnumerable<IEnumerable<T>> source)
{
var result = new List<T>();
var sequences = source.ToList();
for (int sequenceIdx = 0; sequenceIdx < sequences.Count(); sequenceIdx++)
{
var sequence = sequences[sequenceIdx];
for (int targetSequenceIdx = sequenceIdx + 1; targetSequenceIdx < sequences.Count; targetSequenceIdx++)
{
var targetSequence = sequences[targetSequenceIdx];
var intersections = sequence.Intersect(targetSequence);
result.AddRange(intersections);
}
}
return result.Distinct();
}
How it works?
Input: {/*0*/ { 1, 2, 3, 4, 5, 7 } ,/*1*/ { 5, 6, 7 },/*2*/ { 2, 6, 7, 9 } , /*3*/{ 4 } }
Step 0: Intersect 0 with 1..3
Step 1: Intersect 1 with 2..3 (0 with 1 already has been intersected)
Step 2: Intersect 2 with 3 (0 with 2 and 1 with 2 already has been intersected)
Return: Distinct elements.
Result: { 2, 4, 5, 6, 7 }
You can test it with the below code
var lists = new List<List<int>>
{
new List<int> {1, 2, 3, 4, 5, 7},
new List<int> {5, 6, 7},
new List<int> {2, 6, 7, 9},
new List<int> {4 }
};
var result = lists.UnionOfIntersects();
You can try this approach, it might be more efficient and also allows to specify the minimum intersection-count and the comparer used:
public static IEnumerable<T> UnionOfIntersects<T>(this IEnumerable<IEnumerable<T>> source
, int minIntersectionCount
, IEqualityComparer<T> comparer = null)
{
if (comparer == null) comparer = EqualityComparer<T>.Default;
foreach (T item in source.SelectMany(s => s).Distinct(comparer))
{
int containedInHowManySequences = 0;
foreach (IEnumerable<T> seq in source)
{
bool contained = seq.Contains(item, comparer);
if (contained) containedInHowManySequences++;
if (containedInHowManySequences == minIntersectionCount)
{
yield return item;
break;
}
}
}
}
Some explaining words:
It enumerates all unique items in all sequences. Since Distinct is using a set this should be pretty efficient. That can help to speed up in case of many duplicates in all sequences.
The inner loop just looks into every sequence if the unique item is contained. Thefore it uses Enumerable.Contains which stops execution as soon as one item was found(so duplicates are no issue).
If the intersection-count reaches the minum intersection count this item is yielded and the next (unique) item is checked.
That should nail it:
int[][] test = { new int[] { 1, 2, 3, 4, 5, 7 }, new int[] { 5, 6, 7 }, new int[] { 2, 6, 7, 9 }, new int[] { 4 } };
var result = test.SelectMany(a => a.Distinct()).GroupBy(x => x).Where(g => g.Count() > 1).Select(y => y.Key).ToList();
First you make sure, there are no duplicates in each sequence. Then you join all sequences to a single sequence and look for duplicates as e.g. here.
I have an ordered list, largest to smallest.
{ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }
I want a list like this
{ 10, 8, 6, 4, 2, 1, 3, 5, 7, 9 }
If you can see, the new order is, first by each odd index ascending, then by each even index descending.
The idea is that each half of the list has roughly the same weight. e.g
{ 10, 8, 6, 4, 2 } = 30
{ 1, 3, 5, 7, 9 } = 25
The title is the best I can explain in a sentence what I'm looking for which is why I had trouble finding the answer from Google.
Here is my go in C#. I'll welcome any comment on my attempt but I'm only looking of the algorithms name, if it has one.
var firstHalf = new List<string>();
var secondHalf = new List<string>();
for (int i = 0; i < originalList.Count; i++)
{
if (i % 2 == 1)
{
firstHalf.Add(originalList[i]);
}
else
{
secondHalf.Add(originalList[i]);
}
}
secondHalf.Reverse();
var finalList = new List<string>(firstHalf);
finalList.AddRange(secondHalf);
This might not be the most efficient way, but it's easy:
var yourlist = originalList.Where(i => i % 2 == 0)
.OrderBy(i => i)
.Concat(originalList.Where(i => i % 2 != 0)
.OrderByDescending(i => i))
.ToList();