Linq Get Коллекция расположенных рядом одинаковых объектов - PullRequest
1 голос
/ 03 июня 2011

Следующий код показывает, что я хочу сделать:

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 };
}

Я использую только пользовательские реализации, подобные этой, если соответствующая реализация уже не существует.Есть ли способ сделать то же самое со стандартной структурой?

Ответы [ 4 ]

3 голосов
/ 07 ноября 2012

Вы можете использовать SequenceEqual, который «Определяет, равны ли две последовательности, сравнивая элементы с помощью средства сравнения по умолчанию для их типа», если вы имеете дело с упорядоченными коллекциями http://msdn.microsoft.com/en-us/library/bb348567.aspx

В противном случае collection1.Intersect (collection2) .Count () == collection1.Count

сделает свое дело

3 голосов
/ 03 июня 2011

Не существует традиционного способа сделать это со стандартной структурой.У меня есть пара проблем с вашим решением, хотя.

  1. Использование ElementAt(i) очень неэффективно и может привести к многократному повторению коллекции that.Это может привести к проблемам с производительностью
  2. Использование Count также может быть дорогостоящим, поскольку может привести к полному перечислению that
  3. В отличие от большинства методов LINQ, в нем не используется отложенное выполнение,Чтобы это исправить, вам нужно использовать решение в стиле yield return.

Вот альтернативное решение

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);
  }
}
0 голосов
/ 03 июня 2011

Это можно сделать в цепочке операторов.Я не уверен, если бы я посоветовал такой код, хотя!

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));
}
0 голосов
/ 03 июня 2011

Используйте метод расширения Any ()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...