C# - よく使うクラス - List - List から List

クラウディア 
1. 概要
2. 一部の要素で List 作成
3. ソートして別のリストを作成
4. 重複削除
5. グループ化

1. 概要

 他のページで、「List」のコピーについて書いていますが。  ここでは、「List」から別のリストを作成する方法について・・・。  本ページは、下記のサイトを参考にさせていただきました。
Listから重複した要素を削除するには?[C#/VB]」
「【LINQ】任意のクラスでDistinctを使う方法」
「【LINQ】GroupByを使ってリストを分類する

2. 一部の要素で List 作成


public class myClass
{
	public int 	  age  = 0;
	public string name = String.Empty;
}

List<myClass> list01 = new List<myClass>
	{
		new myClass { age = 22, name = "hogehoge" },
		new myClass { age = 21, name = "fugafuga" },
		new myClass { age = 20, name = "piyopiyo" },
	};
 てな状態のクラスがあるとして、これのうち「name」要素から「List<string>」を作成しようとすると、長ったらしくは

List<string> list02 = new List<string>();

foreach (myClass item in list01)
{
	list02.Add(item.name);
}
 となりますが、下の3行は、まとめて、下記の1行にできます。  すっきりして読みやすくていいですね。

list01.ForEach(x => { list02.Add(x.name); });

3. ソートして別のリストを作成

 前項のリストをソートして別のリストを作成します。  「name」の順にしますと

List<myClass> list02 = list01.OrderBy(x => x.name).ToList();
 てな書き方ができます。

foreach (myClass m in list02)
{
    Console.WriteLine("age = [" + m.age + "] name[" + m.name + "]");
}
 で出力すると

age = [21] name[fugafuga]
age = [22] name[hogehoge]
age = [20] name[piyopiyo]
 と、先頭とその次が入れ替わっていますね。  気をつけなければならないのは、「OrderBy」を含んだ行をいれると、「using」を1行追加しないと

error CS1061: Type 'System.Collections.Generic.List<myClass>' does not contain a definition for 'OrderBy' and no extension method 'OrderBy' of type 'System.Collections.Generic.List<myClass>' could be found. Are you missing `System.Linq' using directive?
/usr/local/lib/mono/4.5/mscorlib.dll (Location of the symbol related to previous error)
 てなエラーになります。  突如

using System.Linq;
 が必要になるのですな。  ここいらのしかけがよくわかっていない。

4. 重複削除

 これも「using System.Linq;」が必要です。  「Distinct」というメソッドを使います。

List<string> list01 = new List<string>{ "あ", "あ", "い", "う", "え", "お", "あ", "お", };

var list02 = list01.Distinct();

list01.ForEach(x =>  Console.Write(x));

Console.WriteLine();

foreach (var item in list02)
{
	Console.Write(item);
}

Console.WriteLine();
 上記のソースでロードモジュールを作成して、実行すると下記の結果が得られます。

ああいうえおあお
あいうえお
 これもよくわからないのは、返す型は、「var」になるという・・・。  「var」の箇所を「List<string>」へ変えると

error CS0266: Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<string>' to 'System.Collections.Generic.List<string>'. An explicit conversion exists (are you missing a cast?)
 てな、エラーになっちゃいます。  また、これが単純な「string」の配列なので、簡単なのですが。  複雑なクラスの配列の場合、等価を判断する「IEquatable」てなものを実装する必要があるらしい。  もしくは、「Sort」時に、「Compare」を実装したクラスを引数として渡すように、「IEqualityComparer」を実装したクラスを「Distinct」へ渡すといいらしい。  まだ、やってみてないですけどね。

5. グループ化

 「List」の一部をグループ化して、別のリストを作成することができます。  これも「using System.Linq;」が必要になります。  下記のようなコードを書いてみました。

using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;

public class myClass
{
	public int 	  age  = 0;
	public string name = String.Empty;
}

public class HelloWorld
{
	static public void Main ()
	{
		List<myClass> list01 = new List<myClass>
			{
				new myClass { age = 22, name = "山田 太郎" },
				new myClass { age = 21, name = "山田 次郎" },
				new myClass { age = 20, name = "田中 三郎" },
				new myClass { age = 20, name = "田中 史郎" },
			};


		var SurnameList = list01.GroupBy(x => x.name.Substring(0, 2)).Select(y => new { SurName = y.Key.ToString() });

		foreach (var Surname in SurnameList)
		{
	        Console.WriteLine("Surname = [" + Surname + "]");
		}
	}
}
 名簿から、「姓」が同じ人を抜き出して、グループ化しているわけです。  25行目の「Substring(0, 2)」は、バイト数でなく文字数なのでね。  スマートには、姓と名を半角で分割したいところではあります。  コンパイルして、実行すると下記の結果が得られます。

Surname = [{ SurName = 山田 }]
Surname = [{ SurName = 田中 }]
earthcar(アースカー)
ハイスピードプラン