Группа Linq Chunks [дубликат]

https://www.npmjs.com/package/downloads-folder

Использование

const downloadsFolder = require('downloads-folder');

console.log(downloadsFolder());

Установка

$ npm install downloads-folder

17
задан Dariusz Woźniak 28 February 2013 в 00:32
поделиться

7 ответов

Вы можете использовать Linq GroupBy в модифицированной версии, которая группируется, только если эти два элемента смежны, тогда это легко:

var result = classes
    .GroupAdjacent(c => c.Value)
    .Select(g => new { 
        SequenceNumFrom = g.Min(c => c.SequenceNumber),
        SequenceNumTo = g.Max(c => c.SequenceNumber),  
        Value = g.Key
    });

foreach (var x in result)
    Console.WriteLine("SequenceNumFrom:{0} SequenceNumTo:{1} Value:{2}", x.SequenceNumFrom, x.SequenceNumTo, x.Value);

DEMO

Результат:

SequenceNumFrom:1  SequenceNumTo:2  Value:9
SequenceNumFrom:3  SequenceNumTo:5  Value:15
SequenceNumFrom:6  SequenceNumTo:6  Value:30
SequenceNumFrom:7  SequenceNumTo:7  Value:9

Это метод расширения для группировки смежных элементов:

public static IEnumerable<IGrouping<TKey, TSource>> GroupAdjacent<TSource, TKey>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector)
    {
        TKey last = default(TKey);
        bool haveLast = false;
        List<TSource> list = new List<TSource>();
        foreach (TSource s in source)
        {
            TKey k = keySelector(s);
            if (haveLast)
            {
                if (!k.Equals(last))
                {
                    yield return new GroupOfAdjacent<TSource, TKey>(list, last);
                    list = new List<TSource>();
                    list.Add(s);
                    last = k;
                }
                else
                {
                    list.Add(s);
                    last = k;
                }
            }
            else
            {
                list.Add(s);
                last = k;
                haveLast = true;
            }
        }
        if (haveLast)
            yield return new GroupOfAdjacent<TSource, TKey>(list, last);
    }
}

и используемого класса:

public class GroupOfAdjacent<TSource, TKey> : IEnumerable<TSource>, IGrouping<TKey, TSource>
{
    public TKey Key { get; set; }
    private List<TSource> GroupList { get; set; }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return ((System.Collections.Generic.IEnumerable<TSource>)this).GetEnumerator();
    }
    System.Collections.Generic.IEnumerator<TSource> System.Collections.Generic.IEnumerable<TSource>.GetEnumerator()
    {
        foreach (var s in GroupList)
            yield return s;
    }
    public GroupOfAdjacent(List<TSource> source, TKey key)
    {
        GroupList = source;
        Key = key;
    }
}
15
ответ дан Tim Schmelter 20 August 2018 в 11:19
поделиться
  • 1
    +1 Отличный ответ, это много кода, хотя, я думаю, я бы просто использовал регулярный цикл для каждого цикла и создавал новую коллекцию таким образом – RobJohnson 14 February 2013 в 18:36
  • 2
    Много кода? Это полностью повторное, обобщенное решение. Не так много, учитывая это. Фантастический ответ и новый инструмент для инструментария. +1 – Pete 14 February 2013 в 18:39
  • 3
    Исходным источником этого кода является blogs.msdn.microsoft.com/ericwhite/2008/04/20/… – Quails4Eva 26 September 2016 в 10:54
  • 4
    @ Quails4Eva: я должен признать, что я действительно не знаю, где был исходный источник, я уверен, что это был не я, но я тоже не знаю этого блога. Автор говорит: «Этот подход был предложен одним из архитекторов LINQ год-два назад», поэтому он не настоящий автор. – Tim Schmelter 26 September 2016 в 11:02
  • 5
    @Tim Schmelter Это справедливо. Я бы это интерпретировал, поскольку они предложили подход, и он написал код, или, по крайней мере, версия архива LINQ не является общедоступной. Несмотря ни на что, я не слишком беспокоюсь об этом, мне просто показалось странным найти идентичные решения на расстоянии 5 лет, поэтому я добавил ссылку на более раннюю версию. – Quails4Eva 26 September 2016 в 11:44

Вы можете использовать этот запрос linq

Демо

var values = (new[] { 9, 9, 15, 15, 15, 30, 9 }).Select((x, i) => new { x, i });

var query = from v in values
            let firstNonValue = values.Where(v2 => v2.i >= v.i && v2.x != v.x).FirstOrDefault()
            let grouping = firstNonValue == null ? int.MaxValue : firstNonValue.i
            group v by grouping into v
            select new
            {
              From = v.Min(y => y.i) + 1,
              To = v.Max(y => y.i) + 1,
              Value = v.Min(y => y.x)
            };
3
ответ дан Aducci 20 August 2018 в 11:19
поделиться

Вы можете сделать это следующим образом:

var all = new [] {
    new Cls(1, 9)
,   new Cls(2, 9)
,   new Cls(3, 15)
,   new Cls(4, 15)
,   new Cls(5, 15)
,   new Cls(6, 30)
,   new Cls(7, 9)
};
var f = all.First();
var res = all.Skip(1).Aggregate(
    new List<Run> {new Run {From = f.SequenceNumber, To = f.SequenceNumber, Value = f.Value} }
,   (p, v) => {
    if (v.Value == p.Last().Value) {
        p.Last().To = v.SequenceNumber;
    } else {
        p.Add(new Run {From = v.SequenceNumber, To = v.SequenceNumber, Value = v.Value});
    }
    return p;
});
foreach (var r in res) {
    Console.WriteLine("{0} - {1} : {2}", r.From, r.To, r.Value);
}

Идея состоит в том, чтобы творчески использовать Aggregate: начиная со списка, состоящего из одного Run, проверьте содержимое списка, до сих пор на каждом этапе агрегации (утверждение if в лямбда). В зависимости от последнего значения, продолжайте старый запуск или запустите новый.

Вот демон g0 на ideone .

2
ответ дан dasblinkenlight 20 August 2018 в 11:19
поделиться
  • 1
    ИМХО, лучше использовать цикл foreach, когда в лямбда есть такой код. – juharr 14 February 2013 в 18:38
  • 2
    @juharr Это не просто количество кода, это факт, что он вызывает побочные эффекты и зависит от этих побочных эффектов. Когда важные части любого вызова LINQ вызывают побочные эффекты, обычно это означает, что часть должна быть только в foreach. – Servy 14 February 2013 в 18:42
  • 3
    @Servy Я согласен - я бы не использовал LINQ для запуска обнаружения именно по причине побочных эффектов. Я рассматриваю это как ответ на вопрос о головоломке LINQ, потому что OP запросил LINQ явно. – dasblinkenlight 14 February 2013 в 19:49

Я смог выполнить это, создав собственный метод расширения.

static class Extensions {
  internal static IEnumerable<Tuple<int, int, int>> GroupAdj(this IEnumerable<Cls> enumerable) {
    Cls start = null;
    Cls end = null;
    int value = Int32.MinValue;

    foreach (Cls cls in enumerable) {
      if (start == null) {
        start = cls;
        end = cls;
        continue;
      }

      if (start.Value == cls.Value) {
        end = cls;
        continue;
      }

      yield return Tuple.Create(start.SequenceNumber, end.SequenceNumber, start.Value);
      start = cls;
      end = cls;
    }

    yield return Tuple.Create(start.SequenceNumber, end.SequenceNumber, start.Value);
  }
}

Вот реализация:

static void Main() {
  List<Cls> items = new List<Cls> {
    new Cls { SequenceNumber = 1, Value = 9 },
    new Cls { SequenceNumber = 2, Value = 9 },
    new Cls { SequenceNumber = 3, Value = 15 },
    new Cls { SequenceNumber = 4, Value = 15 },
    new Cls { SequenceNumber = 5, Value = 15 },
    new Cls { SequenceNumber = 6, Value = 30 },
    new Cls { SequenceNumber = 7, Value = 9 }
  };

  Console.WriteLine("From  To    Value");
  Console.WriteLine("===== ===== =====");
  foreach (var item in items.OrderBy(i => i.SequenceNumber).GroupAdj()) {
    Console.WriteLine("{0,-5} {1,-5} {2,-5}", item.Item1, item.Item2, item.Item3);
  }
}

И ожидаемый вывод:

From  To    Value
===== ===== =====
1     2     9
3     5     15
6     6     30
7     7     9
2
ответ дан Joshua 20 August 2018 в 11:19
поделиться

Вот реализация без каких-либо вспомогательных методов:

var grp = 0;
var results =
from i
in
input.Zip(
    input.Skip(1).Concat(new [] {input.Last ()}),
    (n1, n2) => Tuple.Create(
        n1, (n2.Value == n1.Value) ? grp : grp++
    )
)
group i by i.Item2 into gp
select new {SequenceNumFrom = gp.Min(x => x.Item1.SequenceNumber),SequenceNumTo = gp.Max(x => x.Item1.SequenceNumber), Value = gp.Min(x => x.Item1.Value)};

Идея такова:

  • Следите за своим собственным индикатором группировки, grp.
  • Присоедините каждый элемент коллекции к следующему элементу коллекции (через Skip (1) и Zip).
  • Если значения совпадают, они находятся в одной группе; в противном случае приращение grp сигнализирует о начале следующей группы.
2
ответ дан mbeckish 20 August 2018 в 11:19
поделиться

MoreLinq предоставляет эту функциональность из коробки

Она называется GroupAdjacent и реализована как метод расширения на IEnumerable:

] Группирует смежные элементы последовательности в соответствии с заданной функцией выбора ключа.

enumerable.GroupAdjacent(e => e.Key)

Существует даже Nuget «исходный» пакет , который содержит только этот метод, если вы не хотите втягивать дополнительный бинарный пакет Nuget .

Метод возвращает IEnumerable<IGrouping<TKey, TValue>>, поэтому его вывод может обрабатываться так же, как и вывод из GroupBy.

3
ответ дан theDmi 20 August 2018 в 11:19
поделиться
  • 1
    Я думаю, это должно быть отмечено как правильный ответ. Я лично предпочитаю добавлять пакет nuget для копирования / вставки. Кроме того, стоит понять, что еще есть в MoreLinq, которого я отсутствовал. – Mike S. 8 October 2016 в 15:38

Неверная темная магия. Настоящая версия кажется, что в этом случае было бы легче.

IEnumerable<Cls> data = ...;
var query = data
    .GroupBy(x => x.Value)
    .Select(g => new
    {
        Value = g.Key,
        Sequences = g
            .OrderBy(x => x.SequenceNumber)
            .Select((x,i) => new
            {
                x.SequenceNumber,
                OffsetSequenceNumber = x.SequenceNumber - i
            })
            .GroupBy(x => x.OffsetSequenceNumber)
            .Select(g => g
                .Select(x => x.SequenceNumber)
                .OrderBy(x => x)
                .ToList())
            .ToList()
    })
    .SelectMany(x => x.Sequences
        .Select(s => new { First = s.First(), Last = s.Last(), x.Value }))
    .OrderBy(x => x.First)
    .ToList();
1
ответ дан Timothy Shields 20 August 2018 в 11:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: