C# 使用查询表达式对 List<T> 进行排序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/695906/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sort a List<T> using query expressions
提问by
I have a problem using Linq to order a structure like this :
我在使用 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; }
}
A person might go like this:
一个人可能是这样的:
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" };
I would like to use LINQ projection to sort a list of persons ascending by the person attribute of my choice, for example, sort on Age, or sort on FirstName.
我想使用 LINQ 投影按我选择的人员属性对人员列表进行升序排序,例如,按年龄排序,或按名字排序。
I am trying something like
我正在尝试类似的东西
string mySortAttribute = "Age"
PersonList.OrderBy(p => p.PersonAttribute.Find(s => s.Name == mySortAttribute).Value);
But the syntax is failing me. Any clues?
但是语法让我失望。有什么线索吗?
回答by Chris
Why don't you use a key-value dictionary instead of your List<PersonAttribute> ? It would suit better, i think, and make everything else easier.
为什么不使用键值字典而不是 List<PersonAttribute> ?我认为它会更适合,并使其他一切变得更容易。
Update - like this:
更新 - 像这样:
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"]);
回答by Fung
Could it be that your syntax is wrong? Your property is called Attributes but your using something called ObjectSettings in code? Or is that a typo.
可能是你的语法错误?您的属性称为 Attributes 但您在代码中使用了称为 ObjectSettings 的东西?或者这是一个错字。
If it is then your code looks fine unless not all Person instances have the Attribute you're trying to order by in which case you'd get an exception.
如果是,那么您的代码看起来不错,除非并非所有 Person 实例都具有您尝试排序的属性,在这种情况下您会收到异常。
EDIT: Also, instead of using Find, try using First.
编辑:另外,不要使用 Find,而是尝试使用 First。
PersonList.OrderBy(p => p.Attributes.First(a => a.Name == "Age").Value)
回答by Ilya Tchivilev
I'd imagine that you're getting an exception where one item doesn't have an age attribute. I tried the below code, and it worked fine - I'm guessing your data is a bit off, as pointed out by other posters. Anyway, the below works fine...
我想你会得到一个例外,其中一件物品没有年龄属性。我尝试了下面的代码,它运行良好 - 正如其他海报所指出的那样,我猜你的数据有点偏差。无论如何,下面的工作正常......
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();
回答by Rashack
This is assuming that Attribute class implement IComparable or has a nice ToString function (i hope).
这是假设 Attribute 类实现 IComparable 或者有一个很好的 ToString 函数(我希望)。
var list = personList.OrderBy(p => p.Attributes.FirstOrDefault(a => a.Name == "Age"))
Otherwise the syntax gets more convoluted:
否则语法会变得更加复杂:
var list = personList
.OrderBy(p =>
p.Attributes.FirstOrDefault(a => a.Name == "Age") == null ?
"" : p.Attributes.First(a => a.Name == "Age").Value
);
I also assume that you have one value for each key - otherwise you'd need to have smarter code... ;-)
我还假设每个键都有一个值 - 否则你需要更智能的代码...... ;-)
回答by Marc Gravell
OrderBy
is a LINQ extension that produces a new sequence. To order the existing sequence you need to add an extension method or two... then you can use:
OrderBy
是生成新序列的 LINQ 扩展。要订购现有序列,您需要添加一两个扩展方法...然后您可以使用:
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)));
}
}
回答by Jim Schubert
I know this is an old post, but I thought I'd post a comparer I found a while ago in case anyone else needs it.
我知道这是一个旧帖子,但我想我会发布一个我不久前找到的比较器,以防其他人需要它。
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
}
Usage
用法
List<MyObject> objectList = GetObjects(); /* from your repository or whatever */
objectList.Sort(new GenericComparer<MyObject>("ObjectPropertyName", (int)SortDirection.Descending));
dropdown.DataSource = objectList;
dropdown.DataBind();
You could overload the constructor to accept the SortDirection enum. I didn't do this because the class is in a library without a reference to System.Web.
您可以重载构造函数以接受 SortDirection 枚举。我没有这样做是因为该类位于一个没有引用 System.Web 的库中。
回答by Cornelius
Some cases you need to consider:
您需要考虑的一些情况:
- Since your attribute is in string an age of "30" and "3" will be ordered before an age of "4"
- The attribute might not exist
- 由于您的属性在字符串中,因此年龄“30”和“3”将在“4”年龄之前排序
- 该属性可能不存在
If you create this extension methods class:
如果您创建此扩展方法类:
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();
}
}
You can then sort the list correctly in this manner:
然后,您可以以这种方式对列表进行正确排序:
List<Person> persons = ...
...
PersonAttribute defaultAttribute = new PersonAttribute() { Value = "0" };
var ordered = persons.OrderList("Age", defaultAttribute, x => Convert.ToInt32(x));
This will give correct sorting order.
If the attribute will always be present you could remove defaultAttribute
.
这将给出正确的排序顺序。如果该属性始终存在,您可以删除defaultAttribute
.
To sort on 'Name' just use:
要按“名称”排序,只需使用:
List<Person> persons = ...
...
PersonAttribute defaultAttribute = new PersonAttribute() { Value = String.Empty };
var ordered persons.OrderList("Name", defaultAttribute);