15

Possible Duplicate:
Getting odd/even part of a sequence with LINQ
How can I get every nth item from a List<T>?

I'm using HtmlAgilityPack and C# to parse some HTML.

<div id="post-8266">
<div class="ruler"> </div>
<div id="post-8266">
<div class="ruler"> </div>
<div id="post-8266">
<div class="ruler"> </div>
<div id="post-8266">
<div class="ruler"> </div>

Basically, I have these elements, each in their own object, inside of an IEnumerable.

Is there an elegant way to get each N/2 element in the collection. Meaning, skip over each div with class .ruler?

I would need to iterate through the resulting set, so either I copy each found object into a new IEnumerable or just use it inline in a foreach function.

For example:

//Copying resulting set to new IEnumerable<T>:
var odds = elements.SelectOdds();

//Using it inline for my usage:
foreach (var x in elements.SelectOdds())
{   
}

Which options would be best, and how can I achieve this elegantly?

Community
  • 1
  • 1
Only Bolivian Here
  • 35,719
  • 63
  • 161
  • 257
  • possible duplicates: [Getting odd/even part of a sequence with LINQ](http://stackoverflow.com/questions/267033/getting-odd-even-part-of-a-sequence-with-linq), [How can I get every nth item from a List?](http://stackoverflow.com/questions/682615/how-can-i-get-every-nth-item-from-a-listt) – mellamokb Jun 29 '12 at 22:45

2 Answers2

37
var odds = sequence.Where((item, index) => index % 2 != 0);
var evens = sequence.Where((item, index) => index % 2 == 0);

The only thing that I do not like about this solution is that it requires iterating the sequence twice if you need both the odds and the evens. If for some reason you must avoid this, you'll have to work harder:

var groups = sequence.Select((item, index) => new { Item = item, Index = index })
                     .GroupBy(x => x.Index % 2 == 0)
                     .ToDictionary(g => g.Key, g => g);

Then, the odds are those elements of groups where the Key is false, and the evens are those elements of groups where the Key is true:

var odds = groups[false];
var evens = groups[true];
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
jason
  • 236,483
  • 35
  • 423
  • 525
  • 1
    +1 I like the idea behind the second approach. However, I'm really curious if this is faster than the simple version. You dont have to enumerate the original collection twice, but with the creation of the anonymously typed objects, the grouping and the creation of the dictionary, I dont know whether this really pays off... What do you think? – Philip Daubmeier Jun 29 '12 at 22:59
  • @PhilipDaubmeier: I think that depends. If you only `Take(1000)` from the result the first approach might be even faster, at least it wouldn't matter. If you would take all (f.e. by using `Count()`, `foreach` or `ToList()`) the `Dictionary` approach might be significantly faster. – Tim Schmelter Jun 29 '12 at 23:14
  • By the way, the Dictionary approach causes an `OutOfMemoryException` with a hugelist on a PC with 36GB ram whereas the double-`Where` worked always and was really fast if you only take a subset(f.e. `Take(1000)` as mentioned above). – Tim Schmelter Jun 29 '12 at 23:25
  • @Philip Daubmeier: The reason for not enumerating twice is often not speed, although that can be and is a legitimate a concern, but is because some sequences do not produce the same values when iterated twice. – jason Jun 29 '12 at 23:50
  • @Jason: exactly, thats a good point. It may even be the case that a second enumeration produces the same values, but does (due to deferred execution nature of linq) cause the reading and parsing the whole HTML file again (for instance). As a conclusion: both methods have their pros and cons and depend on the specific problem domain. – Philip Daubmeier Jun 30 '12 at 14:30
  • @Tim Schmelter: Thanks for the fix. – jason Jul 05 '12 at 21:33
7

You could just define your own extension method for this purpose:

public static class LinqExtensions
{
    public static IEnumerable<T> SelectOdds<T>(this IEnumerable<T> enumerable)
    {
        bool odd = false;

        foreach (var item in enumerable)
        {
            if (odd)
                yield return item;

            odd = !odd;
        }
    }
}
Philip Daubmeier
  • 14,584
  • 5
  • 41
  • 77