Linqを使用して次のような構造を注文するのに問題があります:
public class Person
{
public int ID { get; set; }
public List<PersonAttribute> Attributes { get; set; }
}
public class PersonAttribute
{
public int ID { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
人はこのように行くかもしれません:
PersonAttribute Age = new PersonAttribute { ID = 8, Name = "Age", Value = "32" };
PersonAttribute FirstName = new PersonAttribute { ID = 9, Name = "FirstName", Value = "Rebecca" };
PersonAttribute LastName = new PersonAttribute { ID = 10, Name = "LastName", Value = "Johnson" };
PersonAttribute Gender = new PersonAttribute { ID = 11, Name = "Gender", Value = "Female" };
LINQプロジェクションを使用して、選択した人物属性で昇順の人物のリストを並べ替えたいと思います。たとえば、年齢で並べ替えたり、FirstNameで並べ替えたりします。
私は次のようなことを試みています
string mySortAttribute = "Age"
PersonList.OrderBy(p => p.PersonAttribute.Find(s => s.Name == mySortAttribute).Value);
しかし、構文は私を失敗させています。手がかりはありますか?
OrderBy
は、新しいシーケンスを生成するLINQ拡張機能です。既存のシーケンスを注文するには、拡張メソッドを1つか2つ追加する必要があります...次に、次を使用できます。
PersonList.Sort(p => p.Attributes.Find(
s => s.Name == mySortAttribute).Value);
public static class ListExtensions {
public static void Sort<TSource, TValue>(
this List<TSource> source,
Func<TSource, TValue> selector)
{
var comparer = Comparer<TValue>.Default;
source.Sort((x, y) => comparer.Compare(selector(x), selector(y)));
}
public static void SortDescending<TSource, TValue>(
this List<TSource> source,
Func<TSource, TValue> selector)
{
var comparer = Comparer<TValue>.Default;
source.Sort((x, y) => comparer.Compare(selector(y), selector(x)));
}
}
私はこれが古い投稿であることを知っていますが、誰かがそれを必要とする場合に備えて、私はしばらく前に見つけた比較者を投稿すると思いました。
public class GenericComparer<T> : IComparer<T>
{
public string SortExpression { get; set; }
public int SortDirection { get; set; } // 0:Ascending, 1:Descending
public GenericComparer(string sortExpression, int sortDirection)
{
this.SortExpression = sortExpression;
this.SortDirection = sortDirection;
}
public GenericComparer() { }
#region IComparer<T> Members
public int Compare(T x, T y)
{
PropertyInfo propertyInfo = typeof(T).GetProperty(SortExpression);
IComparable obj1 = (IComparable)propertyInfo.GetValue(x, null);
IComparable obj2 = (IComparable)propertyInfo.GetValue(y, null);
if (SortDirection == 0)
{
return obj1.CompareTo(obj2);
}
else return obj2.CompareTo(obj1);
}
#endregion
}
使用法
List<MyObject> objectList = GetObjects(); /* from your repository or whatever */
objectList.Sort(new GenericComparer<MyObject>("ObjectPropertyName", (int)SortDirection.Descending));
dropdown.DataSource = objectList;
dropdown.DataBind();
コンストラクターをオーバーロードして、SortDirection列挙型を受け入れることができます。クラスはSystem.Webへの参照のないライブラリにあるため、これは行いませんでした。
List <PersonAttribute>の代わりにKey-Valueディクショナリを使用してみませんか?それはより良く適合し、他のすべてをより簡単にするだろうと私は思います。
更新-このように:
public class Person
{
public Dictionary<string, string> Attributes = new Dictionary<string,string>();
}
List<Person> people = new List<Person>();
Person rebecca = new Person();
rebecca.Attributes["Age"] = "32";
rebecca.Attributes["FirstName"] = "Rebecca";
rebecca.Attributes["LastName"] = "Johnson";
rebecca.Attributes["Gender"] = "Female";
people.Add(rebecca);
var PeopleInAgeOrder = people.OrderBy(p => p.Attributes["Age"]);
これは、AttributeクラスがIComparableを実装しているか、Nice ToString関数を持っていることを前提としています(私は願っています)。
var list = personList.OrderBy(p => p.Attributes.FirstOrDefault(a => a.Name == "Age"))
そうしないと、構文がより複雑になります。
var list = personList
.OrderBy(p =>
p.Attributes.FirstOrDefault(a => a.Name == "Age") == null ?
"" : p.Attributes.First(a => a.Name == "Age").Value
);
また、キーごとに1つの値があると仮定します。そうでない場合は、よりスマートなコードが必要になります... ;-)
考慮する必要があるいくつかのケース:
この拡張メソッドクラスを作成する場合:
public static class ListExtenstions
{
public static List<Person> OrderList(this List<Person> list, string attributeName, PersonAttribute defaultAttribute)
{
return OrderList(list, attributeName, defaultAttribute, x => x);
}
public static List<Person> OrderList<T>(this List<Person> list, string attributeName, PersonAttribute defaultAttribute, Func<string, T> convertion)
{
return list.OrderBy(x => convertion((x.Attributes.FirstOrDefault(y => y.Name == attributeName) ?? defaultAttribute).Value)).ToList();
// Query Syntax
//return
// (from p in list
// let attribute = p.Attributes.FirstOrDefault(a => a.Name == attributeName) ?? defaultAttribute
// orderby attribute.Value
// select p).ToList();
}
}
次に、次の方法でリストを正しく並べ替えることができます。
List<Person> persons = ...
...
PersonAttribute defaultAttribute = new PersonAttribute() { Value = "0" };
var ordered = persons.OrderList("Age", defaultAttribute, x => Convert.ToInt32(x));
これにより、正しい並べ替え順序が得られます。属性が常に存在する場合は、defaultAttribute
を削除できます。
「名前」でソートするには、次を使用します。
List<Person> persons = ...
...
PersonAttribute defaultAttribute = new PersonAttribute() { Value = String.Empty };
var ordered persons.OrderList("Name", defaultAttribute);
構文が間違っている可能性がありますか?プロパティは属性と呼ばれますが、コードでObjectSettingsと呼ばれるものを使用していますか?またはそれはタイプミスです。
その場合、すべてのPersonインスタンスに、注文しようとしている属性が含まれていない限り、コードは正常に見えます。その場合、例外が発生します。
編集:また、検索を使用する代わりに、最初を使用してみてください。
PersonList.OrderBy(p => p.Attributes.First(a => a.Name == "Age").Value)
1つのアイテムに年齢属性がないという例外が発生していると思います。以下のコードを試してみましたが、うまくいきました。他の投稿者が指摘しているように、データが少しずれていると思います。とにかく、以下はうまくいきます...
List<Person> personList = new List<Person>();
Random Rand = new Random();
//generate 50 random persons
for (int i = 0; i < 50; i++)
{
Person p = new Person();
p.Attributes = new List<PersonAttribute>();
p.Attributes.Add(new PersonAttribute() { ID = 8, Name = "Age", Value = Rand.Next(0, 100).ToString() });
p.Attributes.Add(new PersonAttribute() { ID = 10, Name = "Name", Value = Rand.Next(0, 100).ToString() });
personList.Add(p);
}
var finalList = personList.OrderBy(c => c.Attributes.Find(a => a.Name == "Age").Value).ToList();