Linq.GroupByにおけるGroupingのキー項目の動的な指定について
System.Linq.GroupBy()メソッドについて、Groupingのキー項目を
動的に指定するにはどうしたらよいのでしょうか。
対応方法をご存知の方がいらっしゃいましたら、ご教示お願いいたします。
なお、.NET Frameworkは4.5を使用しています。
Groupingの結果、keyは一意になる場合に限り出力し、
valueは、常にサマリ値を出力するという制限を設けております。
// メイン処理
void main()
{
// 初期化
groupByList = new List<GroupByEntity>();
for (int i = 0; i < 10; i++)
{
groupByList.Add(new GroupByEntity()
{
key1 = "key1" + Math.Floor((double)(i / 2)).ToString(),
key2 = "key2" + Math.Floor((double)(i / 3)).ToString(),
key3 = "key3" + Math.Floor((double)(i / 4)).ToString(),
key4 = "key4" + Math.Floor((double)(i / 4) + 2).ToString(),
key5 = "key5" + Math.Floor((double)(i / 5)).ToString(),
value1 = 10 + i,
value2 = 20 + i,
value3 = 30 + i
});
}
outputToConsole(groupByList, "初期値");
// 動的GroupBy
Type groupByType = typeof(GroupByEntity);
ParameterExpression groupByParam = Expression.Parameter(groupByType, "m");
MemberExpression key1Member = Expression.MakeMemberAccess(groupByParam,groupByType.GetMember("key1").First());
MemberExpression key2Member = Expression.MakeMemberAccess(groupByParam,groupByType.GetMember("key2").First());
MemberExpression key3Member = Expression.MakeMemberAccess(groupByParam,groupByType.GetMember("key3").First());
MemberExpression key4Member = Expression.MakeMemberAccess(groupByParam,groupByType.GetMember("key4").First());
MemberExpression key5Member = Expression.MakeMemberAccess(groupByParam,groupByType.GetMember("key5").First());
List<MemberExpression> keyMembers = new List<MemberExpression>();
//---修正前ここから---
//keyMembers.Add(key1Member);
//keyMembers.Add(key2Member);
//
//// ■■このkeyMembersを、下のgroupingにラムダ式として設定したい(以下のように)■■
//---修正前ここまで---
//---修正後ここから---
if (条件1) keyMembers.Add(key1Member);
if (条件2) keyMembers.Add(key2Member);
if (条件3) keyMembers.Add(key3Member);
if (条件4) keyMembers.Add(key4Member);
if (条件5) keyMembers.Add(key5Member);
// ■■このkeyMembersを、下のgroupingにラムダ式として設定したい
// ■■例えば、上記条件1・条件2がtrueとなる場合は、下記groupingと同等になるようにしたい
//---修正後ここまで---
Expression<Func<GroupByEntity, dynamic>> grouping = (m => new { m.key1, m.key2 });
List<GroupByEntity> list = groupByList
.GroupBy(grouping.Compile())
.Select(m => new GroupByEntity()
{
key1 = selectUniqueValue(m.Select(val => val.key1)),
key2 = selectUniqueValue(m.Select(val => val.key2)),
key3 = selectUniqueValue(m.Select(val => val.key3)),
key4 = selectUniqueValue(m.Select(val => val.key4)),
key5 = selectUniqueValue(m.Select(val => val.key5)),
value1 = m.Sum(val => val.value1),
value2 = m.Sum(val => val.value2),
value3 = m.Sum(val => val.value3)
})
.ToList();
outputToConsole(list, "結果");
}
// リスト内の文字列が一意になる場合に限り、その値を戻り値とする
private string selectUniqueValue(IEnumerable<string> list)
{
if (list.Distinct().Count() == 1)
{
return list.First();
}
else
{
return string.Empty;
}
}
// コンソールへの出力
private void outputToConsole(List<GroupByEntity> list, string title)
{
Console.WriteLine(string.Format("---{0}---", title));
foreach (GroupByEntity row in list)
{
Console.WriteLine(row.createOutputLine());
}
Console.WriteLine("------------");
}
// Grouping用属性
public class GroupByEntity
{
public string key1;
public string key2;
public string key3;
public string key4;
public string key5;
public int value1;
public int value2;
public int value3;
// 出力用文字列作成
public string createOutputLine()
{
List<string> ret = new List<string>();
ret.Add(createOutputValue(key1));
ret.Add(createOutputValue(key2));
ret.Add(createOutputValue(key3));
ret.Add(createOutputValue(key4));
ret.Add(createOutputValue(key5));
ret.Add(createOutputValue(value1.ToString()));
ret.Add(createOutputValue(value2.ToString()));
ret.Add(createOutputValue(value3.ToString()));
return string.Join(",", ret);
}
// 出力用文字列整形
public string createOutputValue(string value)
{
return value.PadRight(7);
}
}