LINQ Expression - Dynamic From & Where Clause - c#

I have the following list of integers that I need to extract varying lists of integers containing numbers from say 2-4 numbers in count. The code below will extract lists with only 2 numbers.
var numList = new List<int> { 5, 20, 1, 7, 19, 3, 15, 60, 3, 21, 57, 9 };
var selectedNums = (from n1 in numList
from n2 in numList
where (n1 > 10) && (n2 > 10)
select new { n1, n2 }).ToList();
Is there any way to build up this Linq expression dynamically so that if I wanted lists of 3 numbers it would be compiled as below, this would save me having to package the similar expression inside a different method.
var selectedNums = (from n1 in numList
from n2 in numList
from n3 in numList
where (n1 > 10) && (n2 > 10) && (n3 > 10)
select new { n1, n2, n3 }).ToList();

As with all good questions the way to solve this is with Linq and with Recursion!
public IEnumerable<IEnumerable<T>> Permutation<T>(int count, IEnumerable<T> sequence)
{
if(count == 1)
{
foreach(var i in sequence)
{
yield return new [] { i };
}
yield break;
}
foreach(var inner in Permutation(count - 1, sequence))
foreach(var i in sequence)
{
yield return inner.Concat(new [] { i });
}
}
var foo = Permutation<int>(3, numList.Where(x => x > 10));

[Edited]
You can combine joins and where clauses with loops and conditions, like that:
var q = numList.Where(x => x > 10).Select(x => new List<int>{ x });
for(i = 1; i <= numToTake; ++i)
q = q.Join(numList.Where(x => x > 10), x=> 0, y => 0, (x, y) => { x.Add(y); return x; });
(that returns a List instead of an anonymous object)
Edit 2: Thanks Aron for the comment.

I am not sure if it can help you but an idea how I achieved the same dynamic result based on condition.
var q=(from n in context.TableName
.......);
if(x!=null) //this can be the condition (you can have yours)
var final=q.Select(i=>i....); //I am using q again here
I am not sure what will be the performance.

Related

algorithm searching through dictionary

Im working from the Q https://www.testdome.com/for-developers/solve-question/10282
Write a function that, given a list and a target sum, returns zero-based indices of any two distinct elements whose sum is equal to the target sum. If there are no such elements, the function should return null.
For example, FindTwoSum(new List<int>() { 1, 3, 5, 7, 9 }, 12) should return a Tuple<int, int> containing any of the following pairs of indices:
1 and 4 (3 + 9 = 12)
2 and 3 (5 + 7 = 12)
3 and 2 (7 + 5 = 12)
4 and 1 (9 + 3 = 12)
So far iv got:
class TwoSum
{
public static Tuple<int, int> FindTwoSum(IList<int> list, int sum)
{
//throw new NotImplementedException("Waiting to be implemented.");
IList<int> duplicateList = list;
foreach (int i in list)
{
foreach (int j in duplicateList)
{
if (i != j)
{
if (i + j == sum)
{
return Tuple.Create(i, j);
}
}
}
}
return null;
}
public static void Main(string[] args)
{
Tuple<int, int> indices = FindTwoSum(new List<int>() { 1, 3, 5, 7, 9 }, 12);
Console.WriteLine(indices.Item1 + " " + indices.Item2);
}
}
This returns the correct answer in my code but is failing 3 out of 4 cases in the quesitong because:
Example case: Wrong answer
No solution: Correct answer
One solution: Wrong answer
Performance test with a large number of elements: Wrong answer
Ive looked at the hints
Hint 1: Nested for loops can iterate over the list and calculate a sum in O(N^2) time.
Hint 2: A dictionary can be used to store pre-calculated values, this may allow a solution with O(N) complexity.
So im using nested loops but Im guessing in this instance in order to pass hint2 I need to use a dictionary...How can I refactor this into using a dictionary?
Thanks for any help!
You are not returning indexes, you are returning values. for loops are not foreach loops.
A nested for loops solution would be something like this:
for(int i=0; i<list.Count-1; i++)
{
for(int j=i+1;j<list.Count;j++)
{
if(list[i]+list[j] == sum)
{
return Tuple.Create(i, j);
}
}
}
return null;
I'll leave the dictionary solution for you to create.
Hi this one received 50%
public static Tuple<int, int> FindTwoSum(IList<int> list, int sum)
{
int n = list.Count-1;
while(n != 0)
{
for (int i = 0; i <= list.Count-1 ; i++)
{
if (list[n] + list[i] == sum)
{
return Tuple.Create(i, n);
}
}
n--;
}
return null;
}
// get list value:
var aat = (from l1 in list
from l2 in list
where l1 + l2 == 12
group new { l1, l2} by new { l1, l2 } into gp
select new {gp.Key}).ToDictionary( a => a.Key.l1, b => b.Key.l2 );
// get list index of the value:
var aav = (from l1 in list
from l2 in list
where l1 + l2 == 12
group new { l1, l2 } by new { l1, l2 } into gp
select new { gp.Key })
.ToDictionary( a => list.IndexOf(a.Key.l1), b => list.IndexOf(b.Key.l2)
);

How can I group by the difference between rows in a column with linq and c#?

I want to create a new group when the difference between the values in rows are greater then five.
Example:
int[] list = {5,10,15,40,45,50,70,75};
should give me 3 groups:
1,[ 5,10,15 ]
2,[40,45,50]
3,[70,75]
Is it possible to use Linq here?
Thx!
Exploiting side effects (group) is not a good practice, but can be helpful:
int[] list = { 5, 10, 15, 40, 45, 50, 70, 75 };
int step = 5;
int group = 1;
var result = list
.Select((item, index) => new {
prior = index == 0 ? item : list[index - 1],
item = item,
})
.GroupBy(pair => Math.Abs(pair.prior - pair.item) <= step ? group : ++group,
pair => pair.item);
Test:
string report = string.Join(Environment.NewLine, result
.Select(chunk => String.Format("{0}: [{1}]", chunk.Key, String.Join(", ", chunk))));
Outcome:
1: [5, 10, 15]
2: [40, 45, 50]
3: [70, 75]
Assuming collection has an indexer defined, can be something like this:
const int step = 5;
int currentGroup = 1;
var groups = list.Select((item, index) =>
{
if (index > 0 && item - step > list[index - 1])
{
currentGroup++;
}
return new {Group = currentGroup, Item = item};
}).GroupBy(i => i.Group).ToList();
In my opinion, just write a function to do it. This is easier to understand and more readable than the Linq examples given in other answers.
public static List<List<int>> Group(this IEnumerable<int> sequence, int groupDiff) {
var groups = new List<List<int>>();
List<int> currGroup = null;
int? lastItem = null;
foreach (var item in sequence) {
if (lastItem == null || item - lastItem.Value > groupDiff) {
currGroup = new List<int>{ item };
groups.Add(currGroup);
} else {
// add item to current group
currGroup.Add(item);
}
lastItem = item;
}
return groups;
}
And call it like this
List<List<int>> groups = Group(list, 5);
Assumption: list is sorted. If it is not sorted, just sort it first and use the above code.
Also: if you need groups to be an int[][] just use the Linq Method ToArray() to your liking.

Split large integer array into groups of multiples using one performant LINQ query

I want to sort a large integer array into 2 groups, i.e. 1 group the multiples of 4 and the other group the multiples of 5. How can I do this using just one query? Keep an eye on the performance which is really important in my case.
To further explain what I need, suppose my list of numbers is { 2, 7, 8, 10, 12, 14, 19,20, 25} then I would expect my output to be this:
new[]
{
new
{
Remainder = 4,
Numbers = new List<int>(){ 8, 12, 20}
},
new
{
Remainder = 5,
Numbers = new List<int>(){10, 20, 25}
}
}
Here's what I have gotten so far:
var numberGroupsTimes5 =
from n in numbers
group n by n % 5 into g
where g.Key == 0
select new { Remainder = g.Key, Numbers = g };
var numberGroupsTimes4 =
from n in numbers
group n by n % 4 into g
where g.Key == 0
select new { Remainder = g.Key, Numbers = g };
As you can see it gets me close with 2 queries but as I said I would like a single query.
You could use Concat:
var something = numberGroupsTimes5.Concat(numberGroupsTimes4);
to simply concatenate two sequences.
It's not entire clear why you use a GroupBy, then filter for Key == 0. Remainder will always be 0.
Maybe a simple Where is enough?
You can simply "combine" your queries by using a logical OR (||):
var something = numbers.Where(x => x%4 == 0 || x%5 == 0);
In response to your comment: Are you looking for something like this?
var result = new[] {4, 5}
.Select(d => new
{
Divider = d,
Values = numbers.Where(n => n % d == 0).ToList()
});
Do you mean?
var numberGroupsTimes4or5 = from n in numbers
group n by n into g
where g.Key % 4 == 0 || g.Key % 5 == 0
select new { Remainder = g.Key, Numbers = g };
Maybe this?
var result = new[] { 4, 5 }
.SelectMany(x => numbers.Select(n => (n, x)))
.Where(g => g.n % g.x == 0)
.GroupBy(g => g.x, (Key, g) =>
new { Remainder = Key, Numbers = g.Select(z => z.n) });
which gives this result
Here is a similar approach but this time using a query syntax like in your question.
var numbersAndRemainders = new[] { 4, 5 }
.SelectMany(rem => numbers.Select(n => (n, rem)));
var numberGroups =
from n in numbersAndRemainders
group n by new { remainder = n.n % n.rem, n.rem } into g
where g.Key.remainder == 0
select new { Remainder = g.Key.rem, Numbers = g.Select(z => z.n) };
There are two LINQ methods you could use for this:
//This will join the lists, excluding values that already appear once
var result = numberGroupsTimes5.Union(numberGroupsTimes4)
//This will simply append one list the the other
var result = numberGroupsTimes5.Concat(numberGroupsTimes4)

What is the C# extension methods equivalent for this Linq query?

In this example:
public void Linq40()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var numberGroups =
from n in numbers
group n by n % 5 into g
select new { Remainder = g.Key, Numbers = g };
foreach (var g in numberGroups)
{
Console.WriteLine("Numbers with a remainder of {0} when divided by 5:",
g.Remainder);
foreach (var n in g.Numbers)
{
Console.WriteLine(n);
}
}
}
What is the pure c# equivalent? I get this...
var numberGroups = numbers.GroupBy(n => n % 5)...
but the into clause is a bit of a mystery, and I can't figure out how to get the Key from the Select.
GroupBy returns an IEnumerable<T> of <IGrouping<TKey, TSource>. With this, you can do a second Select operation, which returns values exactly like above:
var numberGroups = numbers.GroupBy(n => n % 5)
.Select(g => new { Remainder = g.Key, Numbers = g });
numbers.GroupBy(n => n % 5).Select(g => new { Remainder = g.Key, Numbers = g });

LINQ method to group collection into subgroups with specified number of elements

Does there exist a LINQ method to group a given collection into subgroups with specified number of elements I mean, something like Scala's grouped method.
e.g. in Scala, List(89, 67, 34, 11, 34).grouped(2) gives List(List(89, 67), List(34, 11), List(34)).
In case such a method doesn't exist, what would be the LINQ way to do it?
Yes, you can. But you can argue if it's very pretty...
Int64[] aValues = new Int64[] { 1, 2, 3, 4, 5, 6 };
var result = aValues
.Select( ( x, y ) => new KeyValuePair<Int64, Int32>( x, y ) )
.GroupBy( x => x.Value / 2 )
.Select( x => x.Select( y => y.Key ).ToList() ).ToList();
How it works:
Select x and y from the original collection, where x is the actual value and y is the index of it in the given collection. Then group by integer devision of the index and the desired grouping length ( in this example 2 ).
Grouping by integer devision will round up to the lower - so 0 / 2 = 0, 1 / 2 = 0, etc. which will give us the needed grouping category value. This is what we are grouping against here.
For result select only the values grouped in lists and return them as a collection of lists.
Here is a website that seems to have some sample code to do what you want:
http://www.chinhdo.com/20080515/chunking/
So what you could do is take this method and create an extension method.
Extension method sample:
static class ListExtension
{
public static List<List<T>> BreakIntoChunks<T>(this List<T> list, int chunkSize)
{
if (chunkSize <= 0)
{
throw new ArgumentException("chunkSize must be greater than 0.");
}
List<List<T>> retVal = new List<List<T>>();
while (list.Count > 0)
{
int count = list.Count > chunkSize ? chunkSize : list.Count;
retVal.Add(list.GetRange(0, count));
list.RemoveRange(0, count);
}
return retVal;
}
}
You could try the approach shown in this answer to this similar question.
public static class GroupingExtension
{
public static IEnumerable<IEnumerable<T>> Grouped<T>(
this IEnumerable<T> input,
int groupCount)
{
if (input == null) throw new ArgumentException("input");
if (groupCount < 1) throw new ArgumentException("groupCount");
IEnumerator<T> e = input.GetEnumerator();
while (true)
{
List<T> l = new List<T>();
for (int n = 0; n < groupCount; ++n)
{
if (!e.MoveNext())
{
if (n != 0)
{
yield return l;
}
yield break;
}
l.Add(e.Current);
}
yield return l;
}
}
}
Use like this:
List<int> l = new List<int>{89, 67, 34, 11, 34};
foreach (IEnumerable<int> group in l.Grouped(2)) {
string s = string.Join(", ", group.Select(x => x.ToString()).ToArray());
Console.WriteLine(s);
}
Result:
89, 67
34, 11
34

Categories