ラムダ式を使った複数キーでの並び替え
解決済
回答 5
投稿
- 評価
- クリップ 2
- VIEW 17K+
前提・実現したいこと
ラムダ式を使って複数のソートキーを組み合わせてListを昇順に並び替えたいです。
このとき、ソートキーはユーザーの設定により変動するものとします。
発生している問題・エラーメッセージ
たとえば、ソートキーを3つ設定(第1ソートキー、第2ソートキー、第3ソートキー)できるとした場合、
3*2 = 6つの並び替えパターンがあると思います。
下記のソースコードのように、
力技で6つのパターンを用意する方法は思いつくのですが、
あまり良いやり方とはおもいません。
(ソートキーの数が増えるとパターンがどんどん増えてしまうので。)
第1ソートキー、第2ソートキー、第3ソートキーを引数として受け取って、
柔軟に並び替えを行うよい方法はないでしょうか?
該当のソースコード
※orderbycaseにはユーザーが設定したソートキーから取得したケース番号が入ると想定します。
public void orderby(int orderbycase, List<User> beforeList)
{
var afterlist = new List<User>();
switch (orderbycase)
{
case 1:
afterlist = beforeList.OrderBy(a => a.name)
.ThenBy(a => a.address)
.ThenBy(a => a.birth).ToList();
break;
case 2:
afterlist = beforeList.OrderBy(a => a.name)
.ThenBy(a => a.birth)
.ThenBy(a => a.address).ToList();
break;
※case 3 ~ 6についてもOrderBy,ThenByメソッドを作成します。
}
}
public class User
{
public string name { get; set; }
public string address { get; set; }
public string birth { get; set; }
}
補足情報(言語/FW/ツール等のバージョンなど)
.NET Framework4.6.1
C#
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
+2
static IOrderedEnumerable<TSource> MyOrderBy<TSource>(this IEnumerable<TSource> list, params Func<TSource, string>[] orders)
{
if (orders.Length == 0)
throw new InvalidOperationException();
IOrderedEnumerable<TSource> result = null;
foreach (var item in orders)
{
if (result == null)
result = list.OrderBy(source => item(source));
else
result = result.ThenBy(source => item(source));
}
return result;
}
上記のような拡張メソッドを定義し、
var users = new List<User>();
var result = users.MyOrderBy(
user => user.address,
user => user.birth,
user => user.name);
などとするのはいかがでしょうか?
拡張メソッドの引数の型Func<TSource, string>
については、
ソートを行うプロパティの型にstring
以外も含まれる場合object
にすればいけるかと。
これによりボックス化が発生しますが、IComparable
が実装されている型であればラムダ式で渡せます。
IComparable
が実装されていない型もソートに使用する場合は、
OrderBy()
,ThenBy()
にIComparer
も渡さなければならないため、別の工夫も必要かと思います。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
checkベストアンサー
+1
こんにちは.
1.ソート手順を配列で定義し,引数に渡せるようにする
2.渡されたソート手順配列をループで回し,得られた要素順にソートを行う
(はじめにafterlistに全要素コピーし,OrderBy関数のみ使用してafterlistを上書きしていく形でソートを重ねる)
ソート手順定義配列の要素の形式は,何も気にしないならば変数名の文字列で良いでしょうし,タイプミスの予防とパフォーマンスを意識するならば,enumにすると良いと思います.
そのうえで,毎回ソート手順の配列を書くのは面倒なので,手順の配列を必要な分だけプリセットとしてstaticな変数で置いておいたり,動的に生成するstatic関数を用意しておいたりして使いまわすといかがでしょうか.
具体的なコードを提示できる時間がないのでヒントのみですみませんが,参考になると幸いです.
※ OrderByのみ使用するにあたって,要素順とは逆順にソートするような対応が必要と推測されます.
※ 当方OrderByを使用した事が無いため,OrderByのみでは達成不可であった場合は申し訳ありません.無視して下さい.
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
無理に、1回のOrderByで済ませようとしなければそれなりにすっきり書けると思います。第3ソートキーでソートしたものを、第2ソートキーでソートし、第1ソートキーでソートすればいいです。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
指定は優先順位として1st、2nd、3rd と順番通りにソートすればいいのではないでしょうか。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
こんな感じでしょうか?
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
static class Program
{
class Person
{
public string Name { get; set; }
public string Address { get; set; }
public DateTimeOffset BirthDay { get; set; }
}
static Person[] People = new[]
{
new Person { Name = "Andrew", Address = "Illinois", BirthDay = DateTimeOffset.Parse("1989-11-02") },
new Person { Name = "George", Address = "Kentucky", BirthDay = DateTimeOffset.Parse("1970-02-11") },
new Person { Name = "Denith", Address = "Cicago", BirthDay = DateTimeOffset.Parse("1984-09-16") },
};
static void Main(string[] args)
{
People.OrderBy(p => p.Name, p => p.Address, p => p.BirthDay).Dump();
People.OrderBy(p => p.Address, p => p.BirthDay, p => p.Name).Dump();
People.OrderBy(p => p.BirthDay, p => p.Name, p => p.Address).Dump();
}
static IEnumerable<Person> OrderBy<TKey1, TKey2, TKey3>(this IEnumerable<Person> source, Func<Person, TKey1> selector1, Func<Person, TKey2> selector2, Func<Person, TKey3> selector3)
{
return source.OrderBy(selector1).ThenBy(selector2).ThenBy(selector3);
}
static void Dump(this IEnumerable<Person> source)
{
foreach (var people in source)
{
Console.WriteLine(people.Name);
}
Console.WriteLine();
}
}
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.33%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2016/06/01 10:54
今回は別の方に提示していただいた方法で実装します。
教えていただいた方法も今後役に立ちそうなので勉強してみます。
2016/06/07 13:57
他の方のように複数回ソートを行うやり方ですと、要素数や条件が増えたら実用になりません。