【C#】時刻文字列を時刻の昇順にソートする


はじめに

以下は、佐賀県小城市(おぎし)のコミュニティバス広域循環バスの時刻表です。

この時刻表では、発車時刻が左から右に並んでいるもの(※赤い矢印)と、右から左に並んでいるもの(※青い矢印)が同じ時刻表に混在しています。
このような表があった時、多くの人は「目線が左から右に動く」と思うので、青い矢印のような表現だと時刻が逆転していて分かりづらく感じると思います。

少々話が飛躍してしまいますが、この時刻表を見て「時刻文字列の順に時刻表のデータをソートする」ことにトライしてみました。

コード

  • LINQを使って簡潔に書くことを目指しました。
  • DepatureTimeクラスは「時刻文字列」と「0時0分からの経過時間(分)」を入れるだけの"箱"のような扱いになっています。
DapartureTime.cs
    /// <summary>
    /// 発時刻での並び替え用クラス。
    /// </summary>
    public class DepatureTimeLogic
    {
        /// <summary>
        /// 引数の発時刻の昇順に並べ替えた結果を返す。
        /// </summary>
        /// <param name="times">発時刻リスト。各要素はHH:MM形式の時刻文字列で表される。</param>
        /// <returns></returns>
        public List<string> Sort(List<string> times)
        {
            // 発時刻クラス型に変換した後、経過時間(分)の昇順でソートして、
            // 最後に時刻文字列に戻してList化している。
            var sortedTimes = times.Select(time => new DepatureTime(time))
                .OrderBy(dTime => dTime.PassageTime)
                .Select(dTime => dTime.TimeString).ToList();

            return sortedTimes;
        }
    }

    /// <summary>
    /// 発時刻クラス。
    /// </summary>
    public class DepatureTime
    {
        /// <summary>
        /// 発時刻。HH:MM形式の時刻文字列で表される。
        /// </summary>
        public string TimeString { get; set; }

        /// <summary>
        /// 経過時間(分)。TimeStringを00:00からの経過時間(分)で表した値。
        /// </summary>
        public int PassageTime { get; set; }

        /// <summary>
        /// コンストラクタ。
        /// </summary>
        /// <param name="timeString">HH:MM形式の時刻文字列。</param>
        public DepatureTime(string timeString)
        {
            this.TimeString = timeString;
            var hhmm = timeString.Split(':');
            this.PassageTime = int.Parse(hhmm[0]) * 60 + int.Parse(hhmm[1]);
        }
    }

テストコード

  • 上記の「佐賀県小城市(おぎし)のコミュニティバスの広域循環バスの時刻表」から、一便目と二便目の時刻をテストに用いました。
    • 一便目:時刻文字列が時刻の昇順に並んでいます。
    • 二便目:時刻文字列が時刻の降順に並んでいます。
DepatureTimeLogicTest
    [TestClass]
    public class DepatureTimeLogicTest
    {
        /// <summary>
        /// 始発(第一便)のバス
        /// </summary>
        private readonly List<string> firstBus = new List<string>(){
            "9:15", "9:19", "9:20", "9:24", "9:32", "9:35", "9:44", "9:47", "9:51", "9:55", "10:07"
        };

        /// <summary>
        /// 第二便のバス
        /// </summary>
        private readonly List<string> secondBus = new List<string>() {
            "11:12", "11:08", "11:07", "11:03", "10:55", "10:52", "10:43", "10:40", "10:36", "10:32", "10:20"
        };

        [TestMethod]
        public void TestMethod1() {
            DepatureTimeLogic logic = new DepatureTimeLogic();
            var sortedFirstBus = logic.Sort(firstBus);
            System.Console.WriteLine("第一便");
            System.Console.WriteLine(string.Join(", ", sortedFirstBus));

            var sortedSecondBus = logic.Sort(secondBus);
            System.Console.WriteLine("第二便");
            System.Console.WriteLine(string.Join(", ", sortedSecondBus));
        }
    }
  • 元から時刻の昇順に並んでいた第一便はそのまま出力されています。
  • 一方で、時刻の降順に並んでいた第二便は、時刻の昇順にソートされていることが分かります。
テスト結果(標準出力)
第一便
9:15, 9:19, 9:20, 9:24, 9:32, 9:35, 9:44, 9:47, 9:51, 9:55, 10:07
第二便
10:20, 10:32, 10:36, 10:40, 10:43, 10:52, 10:55, 11:03, 11:07, 11:08, 11:12