.NET TIPS

オブジェクトのコレクションをプロパティ値によりグループ化するには?[3.5、C#、VB]

デジタルアドバンテージ 遠藤 孝信
2010/03/11

 コレクション内のオブジェクトを、そのオブジェクトの特定のプロパティ値でグループ化(グルーピング)し、グループごとにオブジェクトを列挙するといった処理は、表示処理などでもよく必要となる。

 .NET Framework 3.5以降では、この種のグループ化処理は、LINQを使えば比較的簡単だ。本稿では、グループ化を行うLINQの基本的な記述例を示す。

ファイル一覧を拡張子でグループ化

 以下に示しているサンプル・プログラムは、C:\Windowsにある全ファイルを取得し、それらのファイルを拡張子でグループ化して、拡張子ごとにファイルを一覧表示する。

 サンプル・プログラムを実行すると、次のようなファイル一覧が表示される。

.bin
        ativpsrm.bin
        mib.bin
.dat
        bootstat.dat
        nsreg.dat
.dll
        twain.dll
        twain_32.dll
.exe
        bfsvc.exe
        explorer.exe
        fveupdate.exe
        HelpPane.exe
        hh.exe

……以下省略……
サンプル・プログラムの実行結果(例)

 全ファイルの取得は、DirectoryInfoクラス(System.IO名前空間)のGetFilesメソッドで行える。このメソッドの戻り値は、ファイルの情報を格納したFileInfoクラス(System.IO名前空間)のオブジェクトの配列である。ファイルの拡張子は、Extensionプロパティ、ファイル名はNameプロパティから取得できる。

// grouping1.cs

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

class Program {
  static void Main() {

    var windir = new DirectoryInfo("C:\\Windows");
    FileInfo[] files = windir.GetFiles();

    // LINQのクエリ
    var query = from n in files
                group n by n.Extension into g
                orderby g.Key
                select new { Extension = g.Key, Files = g };

    foreach (var ext in query) {
      Console.WriteLine(ext.Extension); // 拡張子を表示

      foreach (var file in ext.Files) {
        Console.WriteLine("\t" + file.Name); // ファイル名を表示
      }
    }
  }
}

// コンパイル方法:csc grouping1.cs
' grouping1.vb

Imports System.IO
Imports System.Linq

Class Program

  Shared Sub Main()

    Dim windir = New DirectoryInfo("C:\Windows")
    Dim fileInfos As FileInfo() = windir.GetFiles()

    ' LINQのクエリ
    Dim query = From n In fileInfos _
                Group n By ext = n.Extension Into g = Group _
                Order By ext _
                Select New With { .Extension = ext, .Files = g }

    For Each ext In query
      Console.WriteLine(ext.Extension) ' 拡張子を表示

      For Each file In ext.Files
        Console.WriteLine(vbTab & file.Name) ' ファイル名を表示
      Next
    Next
  End Sub
End Class

' コンパイル方法:vbc grouping1.vb
拡張子ごとにファイル名を一覧表示するサンプル・プログラム(上:C#、下:VB)

 最初のポイントとなるのは、グループ化を行うためのgroup句だ。

group n by n.Extension into g
Group n By ext = n.Extension Into g = Group _
グループ化を行うためのLINQのgroup句(上:C#、下:VB)

 コード内の「〜 by n.Extension 〜」の記述より、オブジェクトのシーケンス(=並び)は、Extensionプロパティによりグループ化される。また、「〜 into g 〜」の記述により、各グループのシーケンスは、変数gにより参照できるようになる。

 もう1つのポイントは、グループ化に使用したキー(今回の場合はExtensionプロパティの値)の参照であるが、C#では、グループのKeyプロパティによりキーが取得でき、VBではgroup句の記述時に付けた名前(ここでは「ext」)でキーにアクセスできる。

select new { Extension = g.Key, Files = g };
Select New With { .Extension = ext, .Files = g }
グループ化を行ったキーとグループへのアクセス(上:C#、下:VB)

 このselect句では、グループ化したキーを(匿名クラスの)Extensionプロパティに設定し、グループ化後のシーケンス(FileInfoオブジェクトのコレクション)をFilesプロパティに設定している。

 いずれにせよ、グループのシーケンスまでを一覧表示するには、上記のサンプル・プログラムのように、2重のforeach/For Each文が必要となる。

拡張子の統一とグループ内でのソート

 上記のサンプル・プログラムのクエリは最低限の記述例を示しただけであるため、最後にもう少し機能を追加しておこう。

 1つは、キーの大文字小文字の違いによりグループが異なってしまうため(例:「.INI」と「.ini」)、これを統一する。もう1つは、今回の例では不要だが、グループ内の項目をソートする場合の記述だ。この2点を修正したコードを以下に示す。

// grouping2.cs

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

class Program {
  static void Main() {

    var windir = new DirectoryInfo("C:\\Windows");
    FileInfo[] files = windir.GetFiles();

    // LINQのクエリ
    var query = from n in files
                group n by n.Extension.ToLower() into g
                orderby g.Key
                select new {
                  Extension = g.Key,
                  Files = from m in g
                          orderby m.Name
                          select m
                };

    foreach (var ext in query) {
      Console.WriteLine(ext.Extension); // 拡張子を表示

      foreach (var file in ext.Files) {
        Console.WriteLine("\t" + file.Name); // ファイル名を表示
      }
    }
  }
}

// コンパイル方法:csc grouping2.cs
' grouping2.vb

Imports System.IO
Imports System.Linq

Class Program

  Shared Sub Main()

    Dim windir = New DirectoryInfo("C:\Windows")
    Dim fileInfos As FileInfo() = windir.GetFiles()

    ' LINQのクエリ
    Dim query = From n In fileInfos _
                Group n By ext = n.Extension.ToLower() _
                        Into g = Group _
                Order By ext _
                Select New With { _
                  .Extension = ext, _
                  .Files = _
                    From m In g Order By m.Name Select m _
                }

    For Each ext In query
      Console.WriteLine(ext.Extension) ' 拡張子を表示

      For Each file In ext.Files
        Console.WriteLine(vbTab & file.Name) ' ファイル名を表示
      Next
    Next
  End Sub
End Class

' コンパイル方法:vbc grouping2.vb
拡張子の統一とグループ内でのソートを行ったサンプル・プログラム(上:C#、下:VB)

 このように、各グループ内でのソートは、グループ化の結果(変数g)を利用して、入れ子状にクエリを記述すればよい。End of Article

利用可能バージョン:.NET Framework 3.5以降
カテゴリ:クラス・ライブラリ 処理対象:LINQ
使用ライブラリ:DirectoryInfoクラス(System.IO名前空間)
使用ライブラリ:FileInfoクラス(System.IO名前空間)

この記事と関連性の高い別の.NET TIPS
Windowsフォームでグループ化されたコントロールのタブ・オーダーを設定するには?
[ASP.NET]ListViewコントロールで表示項目をグルーピングするには?
正規表現を使って部分文字列を取得するには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間