开发者

Linq Get Collection of collocated equal objects

The following code shows what I want to do:

public static IEnumerable<IEnumerable<T>> DoIt<T>(this IEnumerable<T> that)
{
    if (that == null)
        throw new ArgumentNullException();

    if (that.Count() > 1)
    {
        var result = new Collection<IEnumerable<T>>();
        var collection = new Collection<T>();

        collection.Add(that.ElementAt(0));
        for (int i = 1; i < that.Count(); ++i)
        {
            if (!that.ElementAt(i).Equals(that.ElementAt(i - 1)))
            {
                result.Add(collection);
                collection = new Collection<T>();
            }

            collection.Add(that.ElementAt(i));
        }

        result.Add(collection);
        return result;
    }

    return new Collection<IEnumerable<T>>() { that };
}

I'm only using custom implementations like that one, if ther开发者_StackOverflow中文版e is no appropriate implementation already existing. Is there any way to do the same with the standard framework?


There is no traditional way to do this with the standard framework. I do have a couple of issues with your solution though.

  1. The use of ElementAt(i) is very inefficient and can cause the that collection to be iterated many, many times. This can lead to performance issues
  2. The use of Count also can be costly as it can cause a full enumeration of that
  3. Unlike most LINQ methods it doesn't use deferred execution. To fix this you will need to use a yield return style solution.

Here's an alternative solution

public static IEnumerable<IEnumerable<T>> DoIt<T>(this IEnumerable<T> that) {
  using (var e = that.GetEnumerator()) {
    if (!e.MoveNext()) {
      yield break;
    }

    bool hasMore;
    do {
      var item = e.Current;
      var list = new List<T>();
      list.Add(item);

      hasMore = e.MoveNext();
      while (hasMore && item.Equals(e.Current)) {
        list.Add(e.Current);
        hasMore = e.MoveNext();
      }

      yield return list;
    } while (hasMore);
  }
}


You can use SequenceEqual which "Determines whether two sequences are equal by comparing the elements by using the default equality comparer for their type" in case you are dealing with ordered collections http://msdn.microsoft.com/en-us/library/bb348567.aspx

Otherwise collection1.Intersect(collection2).Count()==collection1.Count

will do the trick


It is possible to do in a chained statement. I am not sure if I would advise code like this though!

public static IEnumerable<IEnumerable<T>> DoIt<T>(this IEnumerable<T> that) {        
    return that.Zip(that.Skip(1), (a, b) => a.Equals(b) ? 1 : 0)
        .Aggregate(
            (IEnumerable<int>)new []{1, 0}, 
            (c, x) => (new []{c.First() + 1}).Concat(c.Skip(x)), 
            _ => _.Zip(_.Skip(1), (to, skip) => new {skip, take = to - skip}))
        .Reverse()
        .Select(_ => that.Skip(_.skip).Take(_.take));
}


Use the Any() extension method

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜