Реверсирование списка объектов с помощью свойства timespan с сохранением разницы во времени между объектами - PullRequest
1 голос
/ 17 июня 2020

Я хотел бы перевернуть список объектов с помощью свойства TimeSpan, которое должно поддерживать разницу TimeSpan при реверсировании.

В качестве примера рассмотрим маршрут от A до D со следующими TimeSpans: (A 12:00), (B 12:15), (C 12:40), ( D 13:40) .

Между A и B разница в 15 минут, между B и C разница в 25 минут и так далее. Я хотел бы эффективно перевернуть этот список, чтобы список результатов выглядел так: (D: 12:00), (C 13:00), (B 13:25), (A 13:40) .

Моя первая идея заключалась в том, чтобы создать список разницы во времени и использовать это и время начала для создания новых объектов с правильным временем, однако я чувствую, что решение могло бы быть лучше .

Изменить: добавлен мой (рабочий) образец решения. Любые отзывы приветствуются.

        private IList<Activity> ReverseActivities(IList<Activity> activities)
        {
            IList<TimeSpan> timeDifferences = GetTimeDifferences(activities);
            IList<Activity> resultList = new List<Activity>();

            TimeSpan timeOfDay = activities.First().TimeOfDay;

            for (int i = activities.Count - 1; i >= 0; i--)
            {            
                resultList.Add(new Activity(activities[i].Name, timeOfDay));
                timeOfDay = timeOfDay.Add(timeDifferences[i]);
            }

            return resultList;
        }

        private IList<TimeSpan> GetTimeDifferences(IList<Activity> activities)
        {
            IList<TimeSpan> timeDifferences = new List<TimeSpan>();
            Activity prev = activities.First();

            if (activities.Count > 1)
            {
                foreach (var curr in activities)
                {
                    timeDifferences.Add(curr.TimeOfDay - prev.TimeOfDay);
                    prev = curr;
                }
            }

            return timeDifferences;
        }

Действие выглядит следующим образом:

public class Activity
    {
        public Activity(string name, TimeSpan timeOfDay)
        {
            this.Name = name;
            this.TimeOfDay = timeOfDay;
        }

        public string Name { get; }

        public TimeSpan TimeOfDay { get; }
    }

Ответы [ 4 ]

2 голосов
/ 17 июня 2020

Один из приемов, который мы можем использовать, - это иметь один l oop, который находит соответствующий элемент в конце списка на основе текущего индекса. Мы можем сделать это так:

for (int i = 0; i < activities.Count; i++)
    var correspondingIndex = activities.Count - i - 1;

Обратите внимание, что:

  • Когда i равно 0, correspondingIndex - это последний индекс в массиве.
  • Когда i равно 1, correspondingIndex - предпоследний индекс в массиве.
  • Когда i - activities.Count - 1 (последний индекс), correspondingIndex is 0

Используя этот трюк, мы можем получить соответствующие разницы во времени одновременно с заполнением нового списка Activity объектов.

Надеюсь, этот код сделает это немного яснее:

public static IList<Activity> ReverseActivities(IList<Activity> activities)
{
    // If activities is null or contains less than 2 items, return it
    if ((activities?.Count ?? 0) < 2) return activities;

    // This will contain the reversed list
    var reversed = new List<Activity>();

    for (int i = 0; i < activities.Count; i++)
    {
        // Get the corresponding index from the end of the list
        var correspondingIndex = activities.Count - i - 1;

        // Get the timespan from the corresponding items from the end of the list
        var timeSpan = i == 0
            ? TimeSpan.Zero
            : activities[correspondingIndex + 1].TimeOfDay -
                activities[correspondingIndex].TimeOfDay;

        // The new TimeOfDay will be the previous item's TimeOfDay plus the TimeSpan above
        var timeOfDay = i == 0
            ? activities[i].TimeOfDay
            : reversed[i - 1].TimeOfDay + timeSpan;

        reversed.Add(new Activity(activities[correspondingIndex].Name, timeOfDay));
    }

    return reversed;
}

При использовании это будет выглядеть так:

var original = new List<Activity>
{
    new Activity("A", new TimeSpan(0, 12, 0)),
    new Activity("B", new TimeSpan(0, 12, 15)),
    new Activity("C", new TimeSpan(0, 12, 40)),
    new Activity("D", new TimeSpan(0, 13, 40))
};

var reversed = ReverseActivities(original);

Вот результат в окне отладки (сравните original и reversed):

enter image description here

1 голос
/ 17 июня 2020

Это довольно просто, используя небольшую математику TimeSpan.

IList<Activity> input = new List<Activity>()
{
    new Activity("A", TimeSpan.Parse("12:00")),
    new Activity("B", TimeSpan.Parse("12:15")),
    new Activity("C", TimeSpan.Parse("12:40")),
    new Activity("D", TimeSpan.Parse("13:40")),
};

TimeSpan min = input.Min(x => x.TimeOfDay);
TimeSpan max = input.Max(x => x.TimeOfDay);

IList<Activity> output =
    input
        .Select(x => new Activity(
            x.Name,
            x.TimeOfDay.Subtract(max).Duration().Add(min)))
        .OrderBy(x => x.TimeOfDay)
        .ToList();

Это дает мне:

output

0 голосов
/ 17 июня 2020

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

    static void Main(string[] args)
    {
        var list = new List<Location>
        {
            new Location{Name = "A", TimeOffset = DateTimeOffset.MinValue.Add(new TimeSpan(12, 0, 0)) },
            new Location{Name = "B", TimeOffset = DateTimeOffset.MinValue.Add(new TimeSpan(12, 15, 0)) },
            new Location{Name = "C", TimeOffset = DateTimeOffset.MinValue.Add(new TimeSpan(12, 40, 0)) },
            new Location{Name = "D", TimeOffset = DateTimeOffset.MinValue.Add(new TimeSpan(13, 40, 0)) },
        };

        var route = new LinkedList<Location>(list);

        WriteToConsole("Before: ", route);
        var reversedRoute = Reverse(route);
        Console.WriteLine();
        WriteToConsole("After: ", reversedRoute);

        Console.WriteLine(); Console.ReadKey();
    }

    public static LinkedList<Location> Reverse(LinkedList<Location> route)
    {
        LinkedList<Location> retVal = new LinkedList<Location>();

        DateTimeOffset timeOffset = DateTimeOffset.MinValue;
        var currentNode = route.Last;
        while (currentNode != null)
        {
            var next = currentNode.Next;
            if (next == null)
            {
                // last node, use the first node offset
                timeOffset = DateTimeOffset.MinValue.Add(route.First.Value.TimeOffset - timeOffset);
            }
            else
            {
                timeOffset = timeOffset.Add(next.Value.TimeOffset - currentNode.Value.TimeOffset);
            }

            retVal.AddLast(new Location { Name = currentNode.Value.Name, TimeOffset = timeOffset });
            currentNode = currentNode.Previous;
        }
        return retVal;
    }

    public static void WriteToConsole(string title, LinkedList<Location> route)
    {
        Console.Write($"{title}: ");
        foreach (var i in route)
        {
            Console.Write($"\t({i.Name}, {i.TimeOffset.Hour:D2}:{i.TimeOffset.Minute:D2})");
        }
    }
0 голосов
/ 17 июня 2020

Я тестировал это, и он работает:

DateTime[] times = { new DateTime(2020, 06, 17, 12, 00, 00),
    new DateTime(2020, 06, 17, 12, 15, 00), new DateTime(2020, 06, 17, 12, 40, 00),
    new DateTime(2020, 06, 17, 13, 40, 00) };

List<DateTime> newTimes = new List<DateTime>();
newTimes.Add(times[0]);
for (int i = 1; i < times.Length; i++) {
    DateTime d = newTimes[i - 1].Add(times[times.Length - i] - times[times.Length - i - 1]);
    newTimes.Add(d);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...