Reputation: 9360
Is it possible if i have an enumerable
Input :[1,2,3,4]
Output :([1,3],[2,4])
to iterate once over it and start putting odd numbers in a sequence/list/whatever and even in another and at the end placing the two sequences in a tuple?
I have tried something like:
class Elem
{
int x;
}
(rez1,rez2)=from value in Generator()
let odds=Enumerable.Empty<Elem>()
let evens=Enumerable.Empty<Elem>()
select(value%2==0?odds.**append**(value):odds.**append**(value))
How can i direct the select over one element of the tuple or the other for each element?Can i have two mutable lists inside a linq query?
P.S I am trying to achieve something like (similar to Haskell) :
select element -> predicate True -> goes to first element of result tuple
predicate False-> goes to second element of result tuple
Everything in 0(n)
Upvotes: 0
Views: 183
Reputation: 13676
For a list input (not a List<List>>
) we can use GroupBy
to group it by predicate function and Select
to project the IGrouping
to List
:
List<List<int>> buckets = input.GroupBy(func).Select(g => g.ToList()).ToList();
For a List<List>>
input we would need to flatten the list with .SelectMany
before using the samae method as above:
var func = new Func<int, bool>(x => x % 2 == 0);
List<List<int>> buckets = input.SelectMany(e => e).GroupBy(func).Select(g => g.ToList()).ToList();
This approach:
var tuple = Tuple.Create(new List<int>(), new List<int>());
var func = new Func<int, bool>(x => x % 2 == 0);
List<List<int>> buckets = input.Select(e =>
{
func(e)? tuple.Item1.Add(e) : tuple.Item2.Add(e);
return e;
}).ToList();
Is not really a good one.
LINQ is a query language and query is something that should return result not create result. So there is no reason to use Select
for creating elements or adding them to collection, instead we can run our query get the result and save it to whatever object/format we need.
If you need Tuple then save query result of the first two Lists to Tuple
:
var tuple = Tuple.Create(buckets[0], buckets[1]);
Upvotes: 2
Reputation: 45947
Here a approach with 2 List<int>
and only one iteration
List<int> input = new List<int>() { 1, 2, 3, 4 };
List<List<int>> result = new List<List<int>>() { new List<int>(), new List<int>() };
input.ForEach(x => result[x % 2 == 0 ? 0 : 1].Add(x));
Another approach with Linq - a bit slower because of the extra grouping - I would choose the first one
int[][] result = input.GroupBy(x => x % 2).Select(x => x.ToArray()).ToArray();
Upvotes: 5
Reputation: 24913
Something very complicated:
var list = new[] { 1, 2, 3, 4 };
var result = list.Select(
o => new
{
A = o % 2 == 1 ? o : (int?)null,
B = o % 2 == 0 ? o : (int?)null
})
.GroupBy(o => o.A != null)
.Select(o => o.Select(t => t.A ?? t.B ?? 0).ToList())
.ToArray();
Console.WriteLine(result[0]);
Console.WriteLine(result[1]);
Upvotes: 1
Reputation: 1317
If you want to do it like in Haskell, then do it like in Haskell. Haskell does not iterate over a sequence, it defines a tuple elements as sets with certain properties. You should do the same:
var values = Generator();
Tuple.Create(
values.Where( /* define what you want for the first set */ ),
values.Where( /* define what you want for the second set */ ));
Upvotes: 1
Reputation: 2498
Create tuple like the following. Run two queries for odd and even values.
Tuple.Create(input.Where(c => c % 2 == 1).ToArray(), input.Where(c => c % 2 == 0).ToArray());
Upvotes: 1