c# combinations with linq - c#

I have an array of values, e.g. { 0, 1, 2 } which can be in one of two states { 0, 1 }.
Is there a simple way (perhaps using a linq query) to get a list of all combinations of { value, state } (where value is unique) so that I get results like:
{
{ { 0, 0 }, { 1, 0 }, { 2, 0 } },
{ { 0, 0 }, { 1, 0 }, { 2, 1 } },
{ { 0, 0 }, { 1, 1 }, { 2, 0 } },
{ { 0, 0 }, { 1, 1 }, { 2, 1 } },
{ { 0, 1 }, { 1, 0 }, { 2, 0 } },
{ { 0, 1 }, { 1, 0 }, { 2, 1 } },
{ { 0, 1 }, { 1, 1 }, { 2, 0 } },
{ { 0, 1 }, { 1, 1 }, { 2, 1 } },
}
The "value" array can be of varying size, but they can only ever be in one of two states.
(It's not a cartesian product exactly, and I'm not sure what term can be used to describe it, so don't know what to google).
Thanks!

It's a Cartesian product of Cartesian products:
var groups = from x in
(from v in values
from s in states
select new {v,s})
group x by x.v into gx
select gx;
var perms = from a in groups[0]
from b in groups[1]
from c in groups[2]
select new {a,b,c};
The groups query produces a Lookup (conceptually a read-only Dictionary of IEnumerables) containing the simple Cartesian product of all values and states (6 elements), grouped by their value. Then, the second query produces a Cartesian product of the elements of the Cartesian product taken three at a time, one from each group in the Lookup.
To make this work with an unknown number of dimensions would be tricky; if you don't absolutely have to make it work that way I would avoid it. I think the most elegant way would be to define a set of extension methods for the System.Tuple generic classes:
public static Tuple<T1,T2> Append(this Tuple<T1> tuple, T2 addend)
{
return Tuple.Create(tuple.Item1, addend);
}
public static Tuple<T1,T2, T3> Append(this Tuple<T1,T2> tuple, T3 addend)
{
return Tuple.Create(tuple.Item1, tuple.Item2, addend);
}
...
Then, you can take these helpers and use them in a looped version of the second query:
var perms = from a in groups[0]
select Tuple.Create(a);
foreach(var group in groups.Skip(1))
perms = from a in perms
from b in group
select a.Append(b);
This will produce an enumerable of Tuples of the required length, containing the elements of the anonymous type produced in the first query (which can be refactored to produce strongly-typed 2-item Tuples if you wish). You may have an issue with using the perms collection variable to refer to collections of ever-growing Tuples; this is the tricky part.

try
var values = new int[]{0,1,2};
var states = new int[]{0,1};
var permutations = from v in values
from s in states
select new {v,s}
you simply need a cross product of two arrays regardless of size of arrays

Maybe something like:
var lst1 = new List<int> { 0, 1, 2 };
var lst2 = new List<int> { 0, 1 };
lst1.ForEach(i => {
Console.WriteLine("{");
lst2.ForEach(j => Console.Write("{ " + i + "," + j + "}"));
Console.WriteLine("}");
});
This won't really be in the format you want; you should build some kind of array(s) and then join it on "," and/or "}, {". This should give you the general idea, though.

As the two states can be seen as bits, you can get the combinations by simply counting and converting the bits to items in an array:
int maxValue = 2;
int[][][] values = Enumerable.Range(0, 1 << maxValue).Select(n =>
Enumerable.Range(0, maxValue + 1).Select(m =>
new[] { m, (n >> (maxValue - m)) & 1 }
).ToArray()
).ToArray();

Related

Compare 2 Lists of numbers in LINQ Method Syntax

I have 2 lists of numbers.
public int[] numbersA = { 0, 2, 4 };
public int[] numbersB = { 1, 3, 5 };
I need output like below
Expected Result
0 is less than 1
0 is less than 3
0 is less than 5
2 is less than 3
2 is less than 5
4 is less than 5
How is it possible through LINQ method syntax?
With Method syntax:
var result = numbersA.SelectMany(c => numbersB, (c, o) => new { c, o })
.Where(d => d.c < d.o)
.Select(v=> v.c + "is less than"+ v.o);
At times, verbosity take precedence over brevity as it is clearer and easier to read in most situation, although maybe a bit longer to type.
There is no direct way to achieve what you want as you use Array instead of List (List has ForEach)
But if you want with Arrays I suggest using Array.ForEach.
int[] numbersA = new int[] { 0, 2, 4 };
int[] numbersB = new int[] { 1, 3, 5 };
Array.ForEach(numbersA, x =>
{
Array.ForEach(numbersB, y =>
{
if (x < y)
Console.WriteLine(x + " is less than " + y);
});
Console.WriteLine(Environment.NewLine);
});
Although this question is nothing more than useless business logic, it looks funny to have a try. My solution is List.Foreach rather than Linq, but it is in only one statement.
static void Main(string[] args)
{
int[] numsA = { 0, 2, 4 };
int[] numsB = { 1, 3, 5 };
numsA.ToList().ForEach((a) =>
{
numsB.Where(b => b > a).ToList()
.ForEach((x) =>
{
Console.WriteLine("{0}>{1}", a, x);
});
});
}
Give this a try:
int[] numbersA = { 0, 2, 4 };
int[] numbersB = { 1, 3, 5 };
var result = numbersA.Select(a => numbersB.Where(b => a < b)
.Select(b => a + " is less than " + b))
.SelectMany(arr => arr)
.ToArray();

Linq : Checking how many times the same value consecutively

it's my first question so if it's not quite clear , you can ask for extra information. Keep in mind that english is not my native language :).
I was wondering if it's possible to have an elegant way for next specification.
I think linq could be a possibility but i haven't got enough experience with the technology to get it to work:).
Remark this is not a homework assignment it's just a way to get an new angle to solve these kind of problems.
I've tried with the aggegrate function, maybe an action could help.
I want to keep track of:
the max times a value occurs in an array consecutively.
Per value it should display the maximum times the value occured consecutively
for example:
we have an array of 6 elements with elements either 0 or 1
0 , 0 , 0 , 1 , 1 ,0 result : 3 times 0 , 2 times 1
0 , 0 , 1 , 1 , 1 ,0 result : 2 times 0 , 3 times 1
0 , 1 , 0 , 1 , 1 ,0 result : 1 time 0 , 2 times 1
0 , 0 , 1 , 1 , 0 ,0 result : 2 times 0 , 2 times 1
Thanks in advance
I don't think Linq being a good way out; but a simple method will do:
// Disclamer: Dictionary can't have null key; so source must not coтtain nulls
private static Dictionary<T, int> ConsequentCount<T>(IEnumerable<T> source) {
if (null == source)
throw new ArgumentNullException("source");
Dictionary<T, int> result = new Dictionary<T, int>();
int count = -1;
T last = default(T);
foreach (T item in source) {
count = count < 0 || !object.Equals(last, item) ? 1 : count + 1;
last = item;
int v;
if (!result.TryGetValue(last, out v))
result.Add(last, count);
else if (v < count)
result[item] = count;
}
return result;
}
Tests:
int[][] source = new int[][] {
new[] { 0, 0, 0, 1, 1, 0 },
new[] { 0, 0, 1, 1, 1, 0 },
new[] { 0, 1, 0, 1, 1, 0 },
new[] { 0, 0, 1, 1, 0, 0 }, };
string report = string.Join(Environment.NewLine, source
.Select(array => $"{string.Join(" , ", array)} result : " +
string.Join(", ",
ConsequentCount(array)
.OrderBy(pair => pair.Key)
.Select(pair => $"{pair.Value} times {pair.Key}"))));
Console.Write(report);
Outcome:
0 , 0 , 0 , 1 , 1 , 0 result : 3 times 0, 2 times 1
0 , 0 , 1 , 1 , 1 , 0 result : 2 times 0, 3 times 1
0 , 1 , 0 , 1 , 1 , 0 result : 1 times 0, 2 times 1
0 , 0 , 1 , 1 , 0 , 0 result : 2 times 0, 2 times 1
You can write your own method for groupping by consecutive
public static class Extension
{
public static IEnumerable<IEnumerable<int>> GroupConsecutive(this IEnumerable<int> list)
{
var group = new List<int>();
foreach (var i in list)
{
if (group.Count == 0 || i - group[group.Count - 1] == 0)
group.Add(i);
else
{
yield return group;
group = new List<int> {i};
}
}
yield return group;
}
}
And then use it like that:
var groups = new[] { 0, 0, 1, 1, 0, 0 }.GroupConsecutive();
var maxGroupped = groups.GroupBy(i => i.First()).Select(i => new
{
i.Key,
Count = i.Max(j => j.Count())
});
foreach (var g in maxGroupped)
Console.WriteLine(g.Count + " times " + g.Key);
Here is a lazy inefficient way (seems like linear complexity to me):
int[] arr = { 0, 0, 0, 1, 1, 0 };
string str = string.Concat(arr); // "000110"
int max0 = str.Split('1').Max(s => s.Length); // 3
int max1 = str.Split('0').Max(s => s.Length); // 2
and here is the efficient O(n) version:
int[] arr = { 0, 1, 1, 0, 0, 0 };
int i1 = 0, i = 1;
int[] max = { 0, 0 };
for (; i < arr.Length; i++)
{
if (arr[i] != arr[i1])
{
if (i - i1 > max[arr[i1]]) max[arr[i1]] = i - i1;
i1 = i;
}
}
if (i - i1 > max[arr[i1]]) max[arr[i1]] = i - i1;
Debug.Print(max[0] + "," + max[1]); // "3,2"
Linq will be a bit ugly, but it is possible though, your choice of Aggregate is the way to go, but it won't be a one liner in any case,
Something like this will work,
static void Main(string[] args)
{
var list = new List<int>() { 0, 0, 1, 1, 1, 0, 0 };
var result = list.Aggregate(new
{
Last = (int?)null,
Counts = new Dictionary<int, int>(),
Max = new Dictionary<int, int>()
}, (context, current) =>
{
int count;
if (!context.Counts.TryGetValue(current, out count))
count = 1;
if (context.Last == current)
count += 1;
int lastMax;
context.Max.TryGetValue(current, out lastMax);
context.Max[current] = Math.Max(lastMax, count);
if (context.Last != current)
count = 1;
context.Counts[current] = count;
return new { Last = (int?)current, context.Counts, context.Max };
});
Console.WriteLine(string.Join(",", list) + " Result: ");
var output = string.Join(", ", result.Max.Select(x => string.Format("{0} times {1}", x.Value, x.Key)));
Console.WriteLine(output);
Console.ReadKey();
}
Like others said, performance wise Linq might not be the right tool for the job.
My linq only version would be:
from array in arrayOfArrays
let result = new {
Zeroes = array.TakeWhile(x => x == 0).Count(),
Ones = array.SkipWhile(x => x == 0).TakeWhile(x => x == 1).Count()
}
select $"{String.Join(", ", array)} result : {result.Zeroes} times 0, {result.Ones} times 1"
I'm not sure if Linq2Objects will be smart here to optimize the query internally. We ARE within the query iterating mutliple times over the array. So like i said in advance there may be a performance hit if you execute this over a lot of arrays. If anyone would care to check performance of this in regards to other non linq solutions.
First of all thanks to everyone who put the time and effort in answering the question.
I've choosen Dmitry Bychenko as a valid answer , he was the first to provide an answer , and it was an elegant answer.
Matthew diserves also credit because he has shown me how the aggregate function works with conditionals.
last but not least the answer of victor was the simplest one. I did enhance it to work with generics .
void Main()
{
var groups = new[] { 0, 0, 1, 1, 0,30,1,1,1,1,1 , 22, 22, 15,15,0,0,0,0,0,0 }.GroupConsecutive();
groups.Dump();
var maxGroupped = groups.GroupBy(i => i.First()).Select(i => new
{
i.Key,
Count = i.Max(j => j.Count())
});
foreach (var g in maxGroupped)
Console.WriteLine(g.Count + " times " + g.Key);
}
public static class Extension
{
public static IEnumerable<IEnumerable<T>> GroupConsecutive<T>(this IEnumerable<T> list)
{
var group = new List<T>();
foreach (var value in list)
{
if (group.Count == 0 || value.Equals(group[group.Count-1]))
group.Add(value);
else
{
yield return group;
group = new List<T> {value};
}
}
yield return group;
}

Sorting a List of Lists by the amount of intergers of the same type they contain

Hey everyone I have a list of lists which I am trying to sort based on how many of the same type of int it contains. Each item in the list of list contains 4 ints, for ex:
[0,1,2,3]
[0,1,1,1]
[0,2,2,2] etc. Im trying to sort the lists based on which contains the most of a specific int. So if I wanted to sort these three the [0,1,1,1] list would be on top since it contains three 1's etc. I was told to use linq to do this, but didnt know how to use linq.orderby with a method. Can someone help me if there is the correct approach? Thanks!
public class EnemyAI : MonoBehaviour
{
List<List<int>> listOfPaths = new List<List<int>>();
public void sortListofLists ()
{
listOfPaths.OrderBy(runeList => runeList.customMethod(runeType)); //rune type is an int
}
public int customMethod(int sort)
{
int holder = 0;
for (int i = 0; i < MethodList.Count; i++)
{
if (MethodList[i] == sort)
{
holder++;
}
}
return holder;
}
Just order descending by count of particular digits:
var list = new List<List<int>>()
{
new List<int> { 0, 1, 2, 3 },
new List<int> { 0, 1, 1, 1 },
new List<int> { 0, 2, 2, 2 }
};
var result = list.OrderByDescending(c => c.Count(y => y == 1)).ToList();
Your problem can be separated into two sub-problems.
1. How to get the count of the most frequent integer in List?
You can use the following LINQ query:
int countOfMostOccurences = arr.GroupBy(x => x).Max(x => x.Count());
2. How to sort a List by a certain rule in descending order:
list = list.SortByDescending(x => rule);
Now, combine it:
List<List<int>> lists = new List<List<int>>
{
new List<int> { 1, 2, 3, 4 },
new List<int> { 1, 3, 3, 3 },
new List<int> { 1, 2, 2, 3 },
new List<int> { 1, 1, 1, 1 }
};
lists = lists.OrderByDescending(x => x.GroupBy(g => g).Max(g => g.Count())).ToList();

The union of the intersects of the 2 set combinations of a sequence of sequences

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.

Linq GroupBy behaves differently for classes and values

Please consider the following code segment:
var list = new string[] { "ab", "ab", "cd", "cd", "cd" };
var groups = list.GroupBy(l => l);
var count = groups.Count();
The results:
count: 2,
groups: [{ Key: "ab", elements: ["ab", "ab"] }, { Key: "cd", elements: ["cd", "cd", "cd"] }]
When I do the same for class X:
public class X
{
public int A { get; set; }
public string B { get; set; }
}
And the same algorithm is used in order to create the grouped results:
var list2 = new X[]
{
new X { A = 1, B = "b1" },
new X { A = 1, B = "b1" },
new X { A = 2, B = "b2" },
new X { A = 2, B = "b2" },
new X { A = 2, B = "b2" },
};
var groups2 = list2.GroupBy(l => l);
var count2 = groups2.Count();
I would expect the same behavior. I would say count2 is 2, and groups2 contains the two different distinct data sets with 2 and 3 elements respectively.
However when I run this, I get 5 as count and a list of groups containing one item each. Why is the different behavior? I would expect the same aggregation algorithm to behave the same.
Thanks in advance for the explanation.
GroupBy uses default equality comparer for the type unless you provide any implementation.The default comparer for reference types only return true if they are same instances, meaning they have same references. If this is not the behaviour you want you have two choices:
Override Equals and GetHashCode methods in your clas
Implement an IEqualityComparer for your type and pass it to GroupBy

Categories