2009-03-30 16 views
13

ho un problema utilizzando LINQ per ordinare una struttura come questa:Ordina una query Lista <T> utilizzando espressioni

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; } 
} 

Una persona potrebbe andare in questo modo:

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" }; 

vorrei usare LINQ proiezione per ordinare una lista di persone che salgono per l'attributo personale della mia scelta, ad esempio, sort su Age, o sort su FirstName.

Sto provando qualcosa di simile

string mySortAttribute = "Age" 
PersonList.OrderBy(p => p.PersonAttribute.Find(s => s.Name == mySortAttribute).Value); 

Ma la sintassi mi sta fallendo. Qualche indizio?

risposta

5

Perché non si utilizza un dizionario valori-chiave al posto del proprio elenco <PersonAttribute>? Sarebbe meglio, credo, e renderebbe tutto più semplice.

Update - in questo modo:

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"]); 
+10

Ciò non rispondere alla sua domanda. Non ha chiesto come ristrutturare i suoi dati, ha chiesto come ordinare una struttura dati (presumibilmente esistente, e probabilmente immutabile eredità). –

0

Potrebbe essere che la sintassi è sbagliato? La tua proprietà si chiama Attributes ma stai utilizzando qualcosa chiamato ObjectSettings nel codice? O è un errore di battitura.

Se è così il tuo codice sembra buono a meno che non tutte le istanze di Person abbiano l'Attributo che stai cercando di ordinare, nel qual caso otterrai un'eccezione.

MODIFICA: Inoltre, anziché utilizzare Trova, provare a utilizzare Primo.

PersonList.OrderBy(p => p.Attributes.First(a => a.Name == "Age").Value) 
+0

Ho corretto l'errore di battitura, ho scoperto che il mio problema era con IQueryable e List, che richiedeva un cast. Grazie per l'aiuto. –

0

Immagino che stai ricevendo un'eccezione in cui un articolo non ha un attributo di età. Ho provato il codice qui sotto, e ha funzionato bene - sto indovinando i tuoi dati è un po 'fuori, come sottolineato da altri manifesti. In ogni caso, il seguito funziona bene ...

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(); 
1

Ciò presuppone che la classe Attribute implementare IComparable o ha una bella funzione di ToString (spero).

var list = personList.OrderBy(p => p.Attributes.FirstOrDefault(a => a.Name == "Age")) 

In caso contrario, la sintassi si complica:

var list = personList 
      .OrderBy(p => 
        p.Attributes.FirstOrDefault(a => a.Name == "Age") == null ? 
        "" : p.Attributes.First(a => a.Name == "Age").Value 
      ); 

suppongo anche che hai un valore per ogni tasto - altrimenti avresti bisogno di avere il codice più intelligente ... ;-)

9

OrderBy è un'estensione LINQ che produce una nuova sequenza. Per ordinare la sequenza esistente è necessario aggiungere un metodo di estensione o due ... allora si può usare:

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))); 
    } 
} 
8

So che questo è un vecchio post, ma ho pensato di postare un operatore di confronto che ho trovato un po ' fa nel caso che qualcun altro ne abbia bisogno.

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 
} 

Uso

List<MyObject> objectList = GetObjects(); /* from your repository or whatever */ 
objectList.Sort(new GenericComparer<MyObject>("ObjectPropertyName", (int)SortDirection.Descending)); 
dropdown.DataSource = objectList; 
dropdown.DataBind(); 

Si potrebbe sovraccaricare il costruttore ad accettare l'enum SortDirection. Non l'ho fatto perché la classe si trova in una libreria senza un riferimento a System.Web.

0

Alcuni casi è necessario considerare:

  • Dal momento che l'attributo è nella stringa un'epoca di "30" e "3" sarà ordinato prima di un'epoca di "4"
  • L'attributo non potrebbe esistere

Se si crea questa classe metodi di estensione:

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(); 
    } 
} 

È quindi possibile in modo rt l'elenco correttamente in questo modo:

List<Person> persons = ... 
... 
PersonAttribute defaultAttribute = new PersonAttribute() { Value = "0" }; 
var ordered = persons.OrderList("Age", defaultAttribute, x => Convert.ToInt32(x)); 

Questo darà l'ordinamento corretto. Se l'attributo sarà sempre presente, è possibile rimuovere defaultAttribute.

Per ordinare il 'Nome' basta usare:

List<Person> persons = ... 
... 
PersonAttribute defaultAttribute = new PersonAttribute() { Value = String.Empty }; 
var ordered persons.OrderList("Name", defaultAttribute);