Rune Jacobsen
Rune Jacobsen

Reputation: 10081

Getting odd/even part of a sequence with LINQ

Say I have a list of all Projects, and that I group them by Category like this:

var projectsByCat = from p in Projects
                    group p by p.Category into g
                    orderby g.Count() descending
                    select new { Category = g.Key, Projects = g };

Now I want to display this as a list in a web page, where first I create the left side div, secondly the right side div. I am ordering by number of Projects in each Category to show the Categories with the most Projects on top - thus I would like to split projectsByCat in two - if I put all the "odd numbered" Categories on the left and the "even numbered" categories on the right, I think I will get a reasonably sane view.

So I thought I could do this to get the odd and even members of projectsByCat:

var oddCategories = projectsByCat.Where((cat, index) => index % 2 != 0);

var evenCategories = projectsByCat.Where((cat, index) => index % 2 == 0);

And it compiles - however, when I run it, I get an exception such as this:

Unsupported overload used for query operator 'Where'.

And I thought I was safe since it compiled in the first place.. ;)

Is there an elegant way to do this? And also, is there an elegant explanation for why my creative use of Where() won't work?

Upvotes: 31

Views: 50635

Answers (8)

Lasse V. Karlsen
Lasse V. Karlsen

Reputation: 391594

The proper way to do this using LINQ, and avoiding multiple enumerations over the input, is to do a grouping or similar on whether each item is even or odd.

A simple way using the overload for Select that mixes in an index coupled with ToLookup gives you what you want:

var oddsAndEvens = input
    .ToList() // if necessary to get from IQueryable to IEnumerable
    .Select((item, index) => new { isEven = index % 2 == 0, item })
    .ToLookup(
        i => i.isEven,
        i => i.item);

This will produce a Lookup<TKey, TElement> data structure that has the following benefit:

If the key is not found in the collection, an empty sequence is returned.

This means that after the above LINQ query you can do:

var evens = oddsAndEvens[true];
var odds = oddsAndEvens[false];

Upvotes: 4

jptrujillol
jptrujillol

Reputation: 1

Using Linq GroupBy Method:

    List<string> lista = new List<string> { "uno", "dos", "tres", "cuatro" };

    var grupoXindices = lista.GroupBy(i => (lista.IndexOf(i) % 2) == 0);
    foreach (var grupo in grupoXindices) 
    {
    Console.WriteLine(grupo.Key);                
    foreach (var i in grupo) Console.WriteLine(i);
    }

Upvotes: 0

Sam Saarian
Sam Saarian

Reputation: 1146

var text = "this is a test <string> to extract odd <index> values after split";
var parts = text.Split(new char[] { '<', '>' });
IEnumerable words = parts.Where(x => parts.ToList().IndexOf(x) % 2 == 1)

words would contain "string" and "index"

Upvotes: 0

Hrishikesh Kulkarni
Hrishikesh Kulkarni

Reputation: 9

You Can find Even odd number without foreach loop

static void Main(string[] args)
{
    List<int> lstnum = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    List<int> lstresult = lstnum.FindAll(x => (x % 2) == 0);

    lstresult.ForEach(x => Console.WriteLine(x));
}

Upvotes: 0

Athul Nalupurakkal
Athul Nalupurakkal

Reputation: 450

You can separate odd and even in your view using linq.

//even 
@foreach (var item in Model.Where((item, index) => index % 2 == 0))
{
     //do the code
}

//odd
@foreach (var item in Model.Where((item, index) => index % 2 != 0))
{
     //do the code
}

Upvotes: 2

bjhamltn
bjhamltn

Reputation: 410

The oddCategories and the evenCategories are backward.

Indexes start a 0 not 1

0 % 2 = 0

0 index is odd.

var oddCategories  = projectsByCat.Where((cat, index) => index % 2 == 0);

var evenCategories = projectsByCat.Where((cat, index) => index % 2 != 0);

Upvotes: 5

Jacob Carpenter
Jacob Carpenter

Reputation: 4132

Note that calling .ToList() twice for the same query is going query the database twice.

It would be much better to cache the result in an intermediate list, then apply your predicate filtering:

var projectsByCat =
    (from p in Projects
    group p by p.Category into g
    orderby g.Count() descending
    select new { Category = g.Key, Projects = g }).ToList();

var oddCategories = projectsByCat.Where((cat, index) => index % 2 != 0);
var evenCategories = projectsByCat.Where((cat, index) => index % 2 == 0);

Upvotes: 26

Mark Cidade
Mark Cidade

Reputation: 100027

If you're using LINQ to SQL or LINQ to Entities you should first fully materialize the results into memory:

var oddCategories  = projectsByCat.ToList().Where((c,i) => i % 2 != 0);
var evenCategories = projectsByCat.ToList().Where((c,i) => i % 2 == 0);

It isn't possible to iterate through results on the database with an indexer without the use of a cursor, which either ORM framework does not do.

Upvotes: 53

Related Questions