Bercovici Adrian
Bercovici Adrian

Reputation: 9360

How to split a list in two while iterating over it and place the results in a tuple using LINQ?

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

Answers (5)

Fabjan
Fabjan

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

fubo
fubo

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

Backs
Backs

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

rs232
rs232

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

selami
selami

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

Related Questions