記事内に広告が含まれています

【ワレコのC#】LINQでグループ化してソートして数をカウントし集計する

この記事は約13分で読めます。
スポンサーリンク

このところ、久しぶりにC#を使ってプログラミングをしている。

EntityFrameworkを使ってデータベースを操作するプログラムだ。

ワテがデータベースを使えるようになったのは、2、3年前なので、まだあまり詳しくはない。

でも、最近では、EntityFrameworkのコードファーストなどを駆使して、自分のやりたいDB操作を自力で出来るようになった。

従来は、SQLコマンドを文字列で作成して、生のクエリーを実行していたのだが、エンティティフレームワークを使うとそんな必要は無い。

LINQの組み合わせでDBを操作出来るのだ。もちろん必要が有れば生クエリも実行出来る。

ワテがデータベースを習得する前は、データベースと言うとオラクルとかMySQLとか言う名前は知っていた。

そう言うデータベースソフトをサーバーコンピューターにインストールしなくてはデータベースは使えないので、何だか大掛かりな印象を持っていた。

なので、データベース自体には興味はあったのだが中々手が出なかった。

ところがレンタルサーバー(Windows VPS)を契約してそこにSQL Server Express 2016とかMySQLなどのデータベースソフトを入れて使ってみるととってもいい感じ。

食わず嫌いと言う感じだったのだ。

ワテでもやれば出来るやん。

当記事では、LINQの小技を紹介したい。

まあ、ワテの備忘録みたいなもんだが。

ちなみにワテが契約したWindows VPSはエイブルネットさん↓。

スポンサーリンク
スポンサーリンク

データベースと言うのは巨大な配列変数みたいなもん

LINQの小技を紹介する前にデータベースに対するワテの印象を紹介したい。

ワテの印象では、つまりまあ、データベースと言うのは、巨大な配列変数みたいなもんだと思えば良いだろう。配列変数と言うよりもリストと言うほうが良いかな。

ただしメモリ上にある普通の変数ではなくて、他のサーバー上にある感じ。

その巨大なリストに自分の好きなデータを書いたり読んだり。

なので、データベース使った事が無い人でも、配列変数やリスト変数を使ってプログラミングを出来る人なら、データベース使う事は非常に簡単だろう。

巨大な配列あるいはリストだと思えば良いからだ。

もちろん、データベースを使う為にはデータベースに接続するための一連の設定が必要になる。

具体的には、生クエリを実行する場合には、

  • ユーザーID
  • パスワード
  • 接続文字列(コネクションストリング)の作成
  • データベースの作成
  • テーブルの作成
  • クエリコマンドの実行
  • クエリ結果の取得

などだ。

EntityFrameworkのコードファーストの場合なら、

  • ユーザーID
  • パスワード
  • クラスの定義
  • LINQでDBを操作

とういう感じで、とっても簡単。

データベースもテーブルも全自動で生成される。

あとからクラスのメンバ変数を追加・削除・改名などの変更をすると、それはテーブルカラムの追加・削除などの変更に相当するが、DatabaseのMigrationコマンドを実行すれば、それらの変更が自動で反映されるし。

 

当初、生クエリを発行していた時には、データベースの操作と言うのはなんだかややこしいなあと思っていたのだが、EntityFrameworkを使い始めたらデータベースは配列みたいなもんだなあと言う印象を持つ様になった。

複数のプロセスから同時にアクセスしても、排他制御はデータベースがやってくれるし、必要なら自分でやっても良いし。

と言う事で、データベース未経験の人は、兎に角、使ってみる事をお勧めする。

でかい配列を扱っていると思えば簡単なもんだ。

さて、前置きが長くなったが、LINQの小技を紹介したい。

LINQでデータをグループ化しソートする

例えばこんなデータが有るとする。

 
public class 会社クラス
{
	public string 都道府県 { get; set; }
	public string 市区町村 { get; set; }
	public string 会社名 { get; set; }
}

public static List<会社クラス> Get会社クラスリスト()
{
	List<会社クラス> 会社クラスリスト = new List<会社クラス>
	{
		new 会社クラス {都道府県="東京都", 市区町村="目黒区" , 会社名="ワレコ商事"},
		new 会社クラス {都道府県="東京都", 市区町村="新宿区" , 会社名="ワレコ工務店"},
		new 会社クラス {都道府県="大阪府", 市区町村="大阪市" , 会社名="株式会社ワレコ"},
		new 会社クラス {都道府県="大阪府", 市区町村="西成区" , 会社名="ワレコ産業"},
		new 会社クラス {都道府県="東京都", 市区町村="目黒区" , 会社名="ワレコ軍"},
		new 会社クラス {都道府県="大阪府", 市区町村="大阪市" , 会社名="ワレコ興行"},
		new 会社クラス {都道府県="大阪府", 市区町村="西成区" , 会社名="ワレコ組"},
		new 会社クラス {都道府県="大阪府", 市区町村="大阪市" , 会社名="ワレクロ"},
		new 会社クラス {都道府県="大阪府", 市区町村="大阪市" , 会社名="ZOZO Wareko"},
		new 会社クラス {都道府県="大阪府", 市区町村="大阪市" , 会社名="ワレ天"},
	};

	return 会社クラスリスト;
}

つまりまあ、データベースに保管している会社のデータだ。

都道府県 市区町村 会社名

の三つの文字列型のカラムがある。現実の場面では、さらに何丁目何番何号とか、電話番号とか従業員数とか、いろんなカラムがあると思うが、この例では単純化している。

ちなみにワテの場合は半角カタカナ文字が大好きだ。

なので変数にも使うし、ワードその他の文書にも良く使う。

市区町村にある会社の数を集計したい

例えばこのデータに対して、目黒区にある会社の数は何社あるのかな?

と言う場合、どうやれば良いのか。

それが今回紹介するやり方だ。

クエリ構文LINQ版

List<会社クラス> 会社クラスリスト = Get会社クラスリスト();

var 会社Query =
	from 会社 in 会社クラスリスト

	// group 会社 by 会社.都道府県  into g	// 一つのキー(都道府県)でgroup化するならこれで良い。
	group 会社 by new                       // 複数のキーでgroup化する場合。
	{
		会社.都道府県,
		会社.市区町村,
	} into g
	orderby g.Key.市区町村 descending
	orderby g.Key.都道府県
	//o select g;	// Count()計算しないならこれで良い。
	select new
	{
		g.Key.都道府県,
		g.Key.市区町村,
		count = g.Count()
	};


var queryResult = 会社Query.ToList();

foreach (var 会社 in queryResult)
{
	Console.WriteLine("   {0}, {1}: {2}社", 会社.都道府県, 会社.市区町村, 会社.count);
}

コード:会社の数を集計するC#ソースコード(クエリ構文LINQ版)

 

その実行結果は以下の通り。

大阪府, 大阪市: 5社
大阪府, 西成区: 2社
東京都, 目黒区: 2社
東京都, 新宿区: 1社

上で紹介したLINQは select, where, group by などをヅラヅラと書いていくSQL文のような記述方法だ。

クエリ構文LINQと言うらしい。

その辺りの説明はこの記事にも書いてある。

【ワレコのC#】LINQのクエリ構文とメソッド構文(ラムダ式LINQ)【解決】
ワテの場合、昔はラムダ式とかLINQとかの記述方法は馴染めなかった。 例えばC#のLINQはこんなやつだ。 List<int> intLst = new List<int>() { 1, 2, 3, 6, 4, 2,

メソッド構文LINQ版

一方、Select().Where. みたいにメソッドをドットで連結する構文もある。

文字通りメソッド構文LINQと言うらしい。

上のクエリ構文LINQをメソッド構文LINQで書いてみた。

var 会社Query2 = 会社クラスリスト
	.GroupBy(g => new { g.都道府県, g.市区町村 })   // 都道府県名でグループ化トして、次に市区町村名でグループ化
	.OrderBy(o => o.Key.都道府県)                   // 都道府県名でソート
	.ThenBy(t => t.Key.市区町村)                    // その次は市区町村名でソート
	.Select(d => new
	{
		都道府県 = d.Key.都道府県,
		市区町村 = d.Key.市区町村,
		count = d.Count()
	})
	.ToList();

for (int i = 0; i < 会社Query2.Count; i++)
{
	var student_i = 会社Query2[i];
	Console.WriteLine("   {0}, {1}: {2}社", student_i.都道府県, student_i.市区町村, student_i.count);
}

コード:会社の数を集計するC#ソースコード(メソッド構文LINQ版)

その実行結果は以下の通り。

大阪府, 西成区: 2社
大阪府, 大阪市: 5社
東京都, 新宿区: 1社
東京都, 目黒区: 2社

最初の例と比較すると、集計した会社の数は同じだが、並びが異なる。

その理由は、最初の例では市区町村でのorderbyにdescending(降順)を入れているが、後者の例では入れていないからだ。

 

今回作成したC#の全ソースコード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
	class Program
	{

		public class 会社クラス
		{
			public string 都道府県 { get; set; }
			public string 市区町村 { get; set; }
			public string 会社名 { get; set; }
		}

		public static List<会社クラス> Get会社クラスリスト()
		{
			List<会社クラス> 会社クラスリスト = new List<会社クラス>
				{
				   new 会社クラス {都道府県="東京都", 市区町村="目黒区" , 会社名="ワレコ商事"},
				   new 会社クラス {都道府県="東京都", 市区町村="新宿区" , 会社名="ワレコ工務店"},
				   new 会社クラス {都道府県="大阪府", 市区町村="大阪市" , 会社名="株式会社ワレコ"},
				   new 会社クラス {都道府県="大阪府", 市区町村="西成区" , 会社名="ワレコ産業"},
				   new 会社クラス {都道府県="東京都", 市区町村="目黒区" , 会社名="ワレコ軍"},
				   new 会社クラス {都道府県="大阪府", 市区町村="大阪市" , 会社名="ワレコ興行"},
				   new 会社クラス {都道府県="大阪府", 市区町村="西成区" , 会社名="ワレコ組"},
				   new 会社クラス {都道府県="大阪府", 市区町村="大阪市" , 会社名="ワレクロ"},
				   new 会社クラス {都道府県="大阪府", 市区町村="大阪市" , 会社名="ZOZO Wareko"},
				   new 会社クラス {都道府県="大阪府", 市区町村="大阪市" , 会社名="ワレ天"},
				};

			return 会社クラスリスト;
		}

		static void Main(string[] args)
		{

			List<会社クラス> 会社クラスリスト = Get会社クラスリスト();

			var 会社Query =
				from 会社 in 会社クラスリスト

				//o group 会社 by 会社.都道府県  into g		// 一つのキー(都道府県)でgroup化するならこれで良い。
				group 会社 by new							// 複数のキーでgroup化する場合。
				{
					会社.都道府県,
					会社.市区町村,
				} into g
				orderby g.Key.市区町村 descending
				orderby g.Key.都道府県
				//o select g;								// Count()計算しないならこれで良い。
				select new
				{
					g.Key.都道府県,
					g.Key.市区町村,
					count = g.Count()
				};


			var queryResult = 会社Query.ToList();

			foreach (var 会社 in queryResult)
			{
				Console.WriteLine("   {0}, {1}: {2}社", 会社.都道府県, 会社.市区町村, 会社.count);
			}

			Console.WriteLine("--------------------");

			var 会社Query2 = 会社クラスリスト
				.GroupBy(g => new { g.都道府県, g.市区町村 })   // 都道府県名でグループ化して、次に市区町村名でグループ化
				.OrderBy(o => o.Key.都道府県)                   // 都道府県名でソート
				.ThenBy(t => t.Key.市区町村)                    // その次は市区町村名でソート
				.Select(d => new
				{
					都道府県 = d.Key.都道府県,
					市区町村 = d.Key.市区町村,
					count = d.Count()
				})
				.ToList();

			for (int i = 0; i < 会社Query2.Count; i++)
			{
				var student_i = 会社Query2[i];
				Console.WriteLine("   {0}, {1}: {2}社", student_i.都道府県, student_i.市区町村, student_i.count);
			}


		}

	}
}

コード:会社の数を集計するC#の全ソースコード(コンソールアプリ)

その実行結果

大阪府, 大阪市: 5社
大阪府, 西成区: 2社
東京都, 目黒区: 2社
東京都, 新宿区: 1社
--------------------
大阪府, 西成区: 2社
大阪府, 大阪市: 5社
東京都, 新宿区: 1社
東京都, 目黒区: 2社
続行するには何かキーを押してください . . .

 

まとめ

自称、C#プログラミングの初心者のワテであるが、数カ月ぶりにC#のプログラミングを行っている。

最近ではLINQが大好きなので、forループなんかはあまり使わない。

その代わりにLINQやラムダ式を駆使して、本来なら複数行に渡る処理を一行で書く事に快感を覚えている。

そんな事をして何かメリットはあるのか?

多分、無い。

まさに、アホやがな。

まあ兎に角、目的のプログラムを短期間で完成させる事が最も重要だ。

乞うご期待。

ワテが契約しているWindows VPSはAblenet

申し込み時にワレコのサイトで見たと言っても特典は付かないだろう。

LINQ関連商品

LinQ 新春特別公演 ~楽詣~(たのしもうで)あけましておめでとうございマ・シ・テ (Blu-ray)

ワテ推薦の独習C#だ。

スポンサーリンク
コメント募集

この記事に関して何か質問とか補足など有りましたら、このページ下部にあるコメント欄からお知らせ下さい。

C#
スポンサーリンク
シェアする
warekoをフォローする
スポンサーリンク

コメント