LINQ を用いたファイル名のソート例

ここでは具体例をあげて、C# のプログラミングで、特に配列やコレクションを扱うときにとても便利な LINQ のメソッド構文の使用例を紹介します。

フォルダ内のファイル名の配列要素のソート

System.IO.Directory クラスの GetFilesメソッドでは パラメータで指定したディレクトリ内のファイル名を返します。

このとき、例えば拡張子 .txt を持つテキストファイルだけのファイル名の配列を受け取る、 といったフィルターの条件なども渡すことができます。

ここでは便宜上「ファイル名」と書いていますが、実際 GetFiles メソッドで取得できるのはフルパスです。

しかし、取得できるファイル名の順序には何も決まりがないので、なんらかの順番でファイル名を取得したいなら、 GetFiles で返された配列に対して、要素の並べ替え操作を明示的に実施する必要があります。

ここではこの並べ替え操作を、LINQ を利用して行うサンプルコードをいくつか紹介します。

ここではシステムディレクトリ C:\Windows 直下にある *.exe ファイル名の配列を取得して、いくつかの条件で並べ替えてみましょう。

LINQ を用いたファイル名のソート (昇順)

まずはファイル名によるソート (並べ替え) です。

ソートは OrderBy メソッドで行います。配列に対して OrderBy を呼ぶと、 配列の要素1つ1つに対してパラメータのラムダ式が評価されます。

using System;
using System.IO;
using System.Linq;

class Program
{
  static void Main(string[] args)
  {
    var files = Directory.GetFiles(@"/Users/user1/dir1", "*.*")
                         .OrderBy(f => f);

    foreach (var f in files)
    {
      Console.WriteLine($"{f}");
    }
  }
}

OrderBy() をつける前は、次の順番でした。 

/Users/user1/dir1/baz.txt
/Users/user1/dir1/foo.txt
/Users/user1/dir1/bar.txt

OrderBy() によって、次の順番になりました。

/Users/user1/dir1/bar.txt
/Users/user1/dir1/baz.txt
/Users/user1/dir1/foo.txt

LINQ を用いたファイル名のソート (降順)

降順でソートするには OrderByDescending メソッドを使います。

using System;
using System.IO;
using System.Linq;

class Program
{
  static void Main(string[] args)
  {
    var files = Directory.GetFiles(@"/Users/user1/dir1", "*.*")
                         .OrderByDescending(f => f);

    foreach (var f in files)
    {
      Console.WriteLine($"{f}");
    }
  }
}
/Users/user1/dir1/foo.txt
/Users/user1/dir1/baz.txt
/Users/user1/dir1/bar.txt

LINQ を用いたファイル名の長さによるソート

ファイル名の長さによって並べ換えるなら、OrderBy にて f => f.Length なるラムダ式を渡して、 ソートキーを文字列の長さにします。

using System;
using System.IO;
using System.Linq;

class Program
{
  static void Main(string[] args)
  {
    var files = Directory.GetFiles(@"/Users/user1/dir1", "*.*")
                         .OrderBy(f => f.Length);

    foreach (var f in files)
    {
      Console.WriteLine($"{f}");
    }
  }
}

実行例は次のようになります。

dotnet run
/Users/user1/dir1/x.txt
/Users/user1/dir1/abc.txt
/Users/user1/dir1/aaa.txt
/Users/user1/dir1/xxxxx.txt

LINQ を用いた複数の条件によるソート

上のファイル名の長さによるソートでは、同じ長さのファイル名同士の並び順が全く定義されていませんでした。

ソートの条件を複数組み合わせるには、OrderBy などに続き、 ThenByThenByDescending を呼び、ソートの条件を指定することで実現できます。

まずファイル名の長さでソートして、続いて同じファイル名内ではファイル名の降順でソートするには次のようにします。

using System;
using System.IO;
using System.Linq;

class Program
{
  static void Main(string[] args)
  {
    var files = Directory.GetFiles(@"/Users/user1/dir1", "*.*")
                         .OrderBy(f => f.Length)
                         .ThenBy(f => f);

    foreach (var f in files)
    {
      Console.WriteLine($"{f}");
    }
  }
}

実行結果は次の通りです。

dotnet run
/Users/user1/dir1/x.txt
/Users/user1/dir1/aaa.txt
/Users/user1/dir1/abc.txt
/Users/user1/dir1/xxxxx.txt

LINQ を用いたファイルの作成日時によるソート

ファイルの作成日時によるソートを行いたい場合は、Directory クラスの GetFiles メソッドを使うより、 DirectoryInfo クラスを使う方が便利です。

using System;
using System.IO;
using System.Linq;

class Program
{
  static void Main(string[] args)
  {
    var files = new DirectoryInfo(@"/Users/user1/dir1")
                            .GetFiles("*.*")
                            .OrderBy(fi => fi.CreationTime)
                            .Select(fi => fi.FullName);
    foreach (var f in files)
    {
      Console.WriteLine($"{f}");
    }
  }
}

実行結果は次の通りでした。

dotnet run
/Users/user1/dir1/x.txt
/Users/user1/dir1/xxxxx.txt
/Users/user1/dir1/aaa.txt
/Users/user1/dir1/abc.txt

この場合、GetFiles メソッドは FileInfo オブジェクトの配列を返します。

このため引き続き呼び出した OrderBy メソッドでは FileInfo オブジェクトを受け取りますので、作成日時 (CreationTime) をキーとしてソートするには、 fi => fi.CreationTime というラムダ式でソートキーを CreationTime にします。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 C# 入門