Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extensions for IEnumerable generic

Tags:

c#

ienumerable

I've got two extensions for IEnumerable:

public static class IEnumerableGenericExtensions
{
    public static IEnumerable<IEnumerable<T>> InSetsOf<T>(this IEnumerable<T> source, int max)
    {
        List<T> toReturn = new List<T>(max);
        foreach (var item in source)
        {
            toReturn.Add(item);
            if (toReturn.Count == max)
            {
                yield return toReturn;
                toReturn = new List<T>(max);
            }
        }
        if (toReturn.Any())
        {
            yield return toReturn;
        }
    }

    public static int IndexOf<T>(this IEnumerable<T> source, Predicate<T> searchPredicate)
    {
        int i = 0;
        foreach (var item in source)
            if (searchPredicate(item))
                return i;
            else
                i++;

        return -1;
    }
}

Then I write this code:

        Pages = history.InSetsOf<Message>(500);
        var index = Pages.IndexOf(x => x == Pages.ElementAt(0));

where public class History : IEnumerable But as a result I've got not '0' as I've expected, but '-1'. I cant understand - why so?

like image 209
Seekeer Avatar asked Nov 24 '11 07:11

Seekeer


2 Answers

When you write Pages.IndexOf(x => x == Pages.ElementAt(0));, you actually run InSetsOf many times, due to deferred execution (aka lazy). To expand:

  • Pages = history.InSetsOf<Message>(500) - this line doesn't run InSetsOf at all.
  • Pages.IndexOf - Iterates over Pages, so it starts executing InSetsOf once.
  • x == Pages.ElementAt(0) - this executes InSetsOf again, once for every element in the collection of Pages (or at least until searchPredicate return true, which doesn't happen here).

Each time you run InSetsOf you create a new list (specifically, a new first list, because you use ElementAt(0)). These are two different objects, so comparison of == between them fails.

An extremely simple fix would be to return a list, so Pages is not a deferred query, but a concrete collection:

Pages = history.InSetsOf<Message>(500).ToList();

Another option is to use SequenceEqual, though I'd recommend caching the first element anyway:

Pages = history.InSetsOf<Message>(500);
var firstPage = Pages.FirstOrDefault();
var index = Pages.IndexOf(x => x.SequenceEqual(firstPage));
like image 179
Kobi Avatar answered Sep 18 '22 01:09

Kobi


Does your class T implement the IComparable? If not, your equality check might be flawed, as the framework does not know exactly when T= T. You would also get by just overriding equals on your class T I would guess.

like image 30
Yngve B-Nilsen Avatar answered Sep 20 '22 01:09

Yngve B-Nilsen