Laden...

[erledigt] GroupBy einer Liste nach unterschiedlichen Parametern

Erstellt von Yeats vor 10 Jahren Letzter Beitrag vor 10 Jahren 942 Views
Y
Yeats Themenstarter:in
102 Beiträge seit 2005
vor 10 Jahren
[erledigt] GroupBy einer Liste nach unterschiedlichen Parametern

Hallo alle zusammen,

Zuerst mal ein wenig Code


public class Person {
	public string FirstName{get;set;}
	public string MiddleName{get;set;}
	public string LastName{get;set;}
	public string AddressStreet{get;set;}
	public string AddressCity{get;set;}
	public string AddressCountry{get;set;}
	public string AddressZipCode{get;set;}
	public int Age{get;set;}
	... // And some more properties
}

....

List<Person> personList = new List<Person>();
for(int i= 0; i<100; i++) {
	personList.Add(new Person(){.....});
}

....

var categories = new List<string>();
categories.Add("FirstName");
categories.Add("AddressCity");
categories.Add("Age");
// And maybe more categories
DoSomeMagic(personList, categories);

....
public void DoSomeMagic(List<Person> list, List<string> categories) {
	var grouped = list.GroupBy(x=>x.AddressCity);
	foreach(var item in grouped) {
		foreach(var person in item) {
			...
		}
	}
}

Wie man sieht habe ich eine Klasse Personen, mit dieser wird eine Liste erstellt. Die Klasse hat mehrere Properties.
Nun möchte ich die Liste der Personen, entsprechend mit einer List mit den Namen von den Properties der Klasse Personen, gruppieren.
Wobei in der Liste Categories die Reihenfolge veränderbar sind und die Anzahl der Kategorien unterschiedlich sein kann.
Die Frage ist nun, wie kann ich entsprechend dem Inhalt in categories eine möglichste elegante Methode aufbauen, die das gewünschte Verhalten erzeugt.

Mfg

4.939 Beiträge seit 2008
vor 10 Jahren

Hallo,

Linq (GroupBy) und Strings (Namen der Eigenschaften) passen nicht so recht zusammen, weil dir so ja nur Reflection bleibt.
Muß denn 'categories' unbedingt eine Liste von Strings sein?
Kannst du denn kein List<Func<Person, string> übergeben? Wobei 'Age' hierbei ausfallen würde, da es ja ein 'int' darstellt, bzw. du daraus einen String basteln könntest "x => x.Age.ToString()" (aber bedenke, daß dann die Gruppierungsreihenfolge auch entsprechend als String und nicht als int angezeigt werden würde).

Du kannst dir natürlich auch ein Dictionary<string, Func<Person, string>> basteln, welches du dann benutzen kannst:


Dictionary<string, Func<Person, string>> map =
{
   { "FirstName", x => x.FirstName },
   { "LastName", x => x.Lastname },
   // ...
}

Und dann mittels der String-Liste 'categories' die Gruppierung zusammenbauen.

C
258 Beiträge seit 2011
vor 10 Jahren

Irgendwie hab ich das noch nicht ganz verstanden, willst du eine Gruppe nach Namen jedes Element in der Gruppierung nochmal Gruppiert in Altersgruppen oder willst du einmal eine Gruppierung nach namen und einmal eine Gruppierung nach Altersgruppen?

Verwechselst du vl GroupBy mit OrderBy, für mich macht eine so verschaltete Gruppierung mit unbekannter Tiefe kaum Sinn

Y
Yeats Themenstarter:in
102 Beiträge seit 2005
vor 10 Jahren

@Th69: Es ist eine Liste von strings

@Console32: Nein, verwechsle hierbei nicht GroupBy mit OrderBy. Benötige eine Gruppierung der Elemente. Der Sinn dazu ist der, dass das UI-Seitig in einem "Tree" dargestellt werden soll. Zugrunde liegt nun mal eine List of Persons. Jede Gruppierung entspricht hierbei einem Node. Die Anzahl und Reihenfolge der Categories kann vom User hierbei beliebig gewählt werden.

Meine Lösung (leider nicht so elegant wie erhofft aber funktionierend):
Mittels einer switch-Anweisung wird zuerst nach dem ersten Element aus der Liste Categories, die List of Persons, mittels GroupBy gruppiert. Anschließend für jedes Element in der List<IGrouping<..>> überprüft ob noch weitere Categories vorhanden sind und für diese ein weiterer Durchlauf gestartet.

mfg

C
258 Beiträge seit 2011
vor 10 Jahren

Wenn der Benutzer die Kategorien ebenfalls über Reflection angeboten bekommt, sehe ich kein Problem mit Reflection zu arbeiten.

in etwa so:

 public static Dictionary<string, Dictionary<string, List<T>>> GroupByProperties<T>(this IEnumerable<T> source, params string[] categories)
        {
            var properties = typeof(T).GetProperties().ToDictionary(keySelector => keySelector.Name);
            return categories.Where(properties.ContainsKey).ToDictionary(item => item, item => source
                .GroupBy(selector => properties[item].GetValue(selector, null))
                .ToDictionary(keySelector => keySelector.Key.ToString(), elementSelector => elementSelector.ToList()));
        }

Gibt dann ein Dictionary zurück das pro Kategorie einen Eintrag enthält, welcher wieder rum pro Gruppierung einen Eintrag und eine Liste vom Typ T enthält.
In meinem Beispiel habe ich die Gruppierungen auf String gecastet da du sie ja nur im Baum anzeigen willst.