Заменить повторяющиеся значения в коллекции на ее сумму - PullRequest
0 голосов
/ 27 сентября 2018

У меня есть список пользовательских классов ModeTime, его структура ниже:

private class ModeTime
{
    public DateTime Date { get; set; }
    public string LineName { get; set; }
    public string Mode { get; set; }
    public TimeSpan Time { get; set; }
}

В этом списке у меня есть несколько предметов, чьи LineName и Mode одинаковы, и онизаписаны в списке один за другим.Мне нужно суммировать Time свойство таких элементов и заменить его одним элементом с суммой Time свойства без изменения LineName и Mode, Date должны быть взяты из первого из замененных элементов.Я приведу пример ниже:

Original:                                   Modified:
Date       | LineName | Mode   | Time               Date | LineName | Mode   | Time
01.09.2018 | Line1    | Auto   | 00:30:00     01.09.2018 | Line1    | Auto   | 00:30:00
01.09.2018 | Line2    | Auto   | 00:10:00     01.09.2018 | Line2    | Auto   | 00:15:00
01.09.2018 | Line2    | Auto   | 00:05:00     01.09.2018 | Line2    | Manual | 00:02:00
01.09.2018 | Line2    | Manual | 00:02:00     01.09.2018 | Line2    | Auto   | 00:08:00
01.09.2018 | Line2    | Auto   | 00:08:00     01.09.2018 | Line1    | Manual | 00:25:00
01.09.2018 | Line1    | Manual | 00:25:00     01.09.2018 | Line2    | Auto   | 00:24:00
01.09.2018 | Line2    | Auto   | 00:05:00     02.09.2018 | Line1    | Auto   | 00:05:00
02.09.2018 | Line2    | Auto   | 00:12:00
02.09.2018 | Line2    | Auto   | 00:07:00
02.09.2018 | Line1    | Auto   | 00:05:00

Я пытался написать метод, чтобы сделать это, он частично работает, но некоторые не суммированные элементы все еще остаются.

private static List<ModeTime> MergeTime(List<ModeTime> modeTimes)
{
    modeTimes = modeTimes.OrderBy(e => e.Date).ToList();
    var mergedModeTimes = new List<ModeTime>();

    for (var i = 0; i < modeTimes.Count; i++)
    {
        if (i - 1 != -1)
        {
            if (modeTimes[i].LineName == modeTimes[i - 1].LineName &&
                modeTimes[i].Mode == modeTimes[i - 1].Mode)
            {
                mergedModeTimes.Add(new ModeTime
                {
                    Date = modeTimes[i - 1].Date,
                    LineName = modeTimes[i - 1].LineName,
                    Mode = modeTimes[i - 1].Mode,
                    Time = modeTimes[i - 1].Time + modeTimes[i].Time
                });
                i += 2;
            }
            else
            {
                mergedModeTimes.Add(modeTimes[i]);
            }
        }
        else
        {
            mergedModeTimes.Add(modeTimes[i]);
        }
    } 

    return mergedModeTimes;
}

У меня также естьпопытался обернуть for с do {} while() и уменьшить длину списка источников modeTimes длина.К сожалению, это приводит к зацикливанию и утечке памяти (я ждал до 5 ГБ памяти).

Надеюсь, кто-нибудь может мне помочь.Я искал эту проблему, в некоторых знакомых случаях люди используют GroupBy.Но я не думаю, что это будет работать в моем случае, я должен суммировать элементы с одинаковыми LineName и Mode, только если они находятся в списке один за другим.

Ответы [ 2 ]

0 голосов
/ 27 сентября 2018

Вы можете использовать LINQ GroupBy.Чтобы сгруппировать только последовательные элементы, используется хитрость.Он хранит значения ключей в кортеже вместе с групповым индексом, который увеличивается только при изменении LineName или Mode.

int i = 0; // Used as group index.
(int Index, string LN, string M) prev = default; // Stores previous key for later comparison.
var modified = original
    .GroupBy(mt => {
        var ret = (Index: prev.LN == mt.LineName && prev.M == mt.Mode ? i : ++i,
                   LN: mt.LineName, M: mt.Mode);
        prev = (Index: i, LN: mt.LineName, M: mt.Mode);
        return ret;
    })
    .Select(g => new ModeTime {
        Date = g.Min(mt => mt.Date),
        LineName = g.Key.LN,
        Mode = g.Key.M,
        Time = new TimeSpan(g.Sum(mt => mt.Time.Ticks))
    })
    .ToList();

Это приводит к ожидаемым 7 строкам результата.

0 голосов
/ 27 сентября 2018

Самое примитивное решение было бы что-то вроде этого.

var items = GetItems();
var sum = TimeSpan.Zero;

for (int index = items.Count - 1; index > 0; index--)
{
    var item = items[index];
    var nextItem = items[index - 1];

    if (item.LineName == nextItem.LineName && item.Mode == nextItem.Mode)
    {
        sum += item.Time;
        items.RemoveAt(index);
    }
    else
    {
        item.Time += sum;
        sum = TimeSpan.Zero;
    }
}
items.First().Time += sum;

Редактировать: я пропустил последнюю строку, где вы должны добавить остатки.Это применимо только в том случае, если первый и второй элементы коллекции равны .Без этого он не назначил бы агрегированное время первому элементу.

...