2017-06-28 7 views
2

Ich versuche, ein Array zu sortieren, das logische Personengruppen und die Bewertungen der Personen enthält.Sortieren von Gruppen basierend auf Werten innerhalb von Gruppen

Die Personen sollten nach Gruppe sortiert werden, basierend auf der niedrigsten Punktzahl in der Gruppe. Daher ist Gruppe 3 die erste, weil sie die Person mit der niedrigsten Punktzahl enthält. Dann die Personen in Gruppe 1, weil sie die Person mit der nächstniedrigeren Punktzahl hat (und eine niedrigere Gruppennummer als Gruppe 2).

So wäre die Folge: Cameron, Boris, Donna, Alfred, Emily

ich das erreicht habe, aber ich frage mich, ob es einen besseren Weg, es zu tun ist. Ich erhalte ein Array und sortiere das Array in der richtigen Reihenfolge.

Ich benutze LINQ (meist erhalten von Linq order by, group by and order by each group?), um ein Zielsortierfeld zu erstellen, das anzeigt, wo eine Person sein sollte, verglichen mit dem, wo sie sich gerade im Array befinden.

Ich verwende dann Array.Sort mit meinem Zielsortierfeld, aber das Array, das die LINQ-Anweisung erstellt, ist in Bezug auf Indizes und Werte "umgekehrt", also muss ich die Indizes und Werte umkehren (nicht die Reihenfolge).

Ich habe meinen Code unten beigefügt. Gibt es einen besseren Weg, dies zu tun?

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace Sorter 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      // Sample person array. 
      // Lower score is better. 
      Person[] peopleArray = new Person[] 
      { 
       new Person { Name = "Alfred", Group = "1", Score = 3, ArrayIndex = 0 }, 
       new Person { Name = "Boris", Group = "3", Score = 3, ArrayIndex = 1 }, 
       new Person { Name = "Cameron", Group = "3", Score = 1, ArrayIndex = 2 }, 
       new Person { Name = "Donna", Group = "1", Score = 2, ArrayIndex = 3 }, 
       new Person { Name = "Emily", Group = "2", Score = 2, ArrayIndex = 4 } 
      }; 

      // Create people list. 
      List<Person> peopleModel = peopleArray.ToList(); 

      // Sort the people based on the following: 
      // Sort people into groups (1, 2, 3) 
      // Sort the groups by the lowest score within the group. 
      // So, the first group would be group 3, because it has the 
      // member with the lowest score (Cameron with 1). 
      // The people are therefore sorted in the following order: 
      // Cameron, Boris, Donna, Alfred, Emily 
      int[] targetOrder = peopleModel.GroupBy(x => x.Group) 
              .Select(group => new 
              { 
               Rank = group.OrderBy(g => g.Score) 
              }) 
              .OrderBy(g => g.Rank.First().Score) 
              .SelectMany(g => g.Rank) 
              .Select(i => i.ArrayIndex) 
              .ToArray(); 

      // This will give the following array: 
      // [2, 1, 3, 0, 4] 
      // I.e: Post-sort, 
      // the person who should be in index 0, is currently at index 2 (Cameron). 
      // the person who should be in index 1, is currently at index 1 (Boris). 
      //     etc. 

      // I want to use my target array to sort my people array. 

      // However, the Array.sort method works in the reverse. 
      // For example, in my target order array: [2, 1, 3, 0, 4] 
      // person currently at index 2 should be sorted into index 0. 
      // I need the following target order array: [3, 1, 0, 2, 4], 
      // person currently at index 0, should be sorted into index 3 
      // So, "reverse" the target order array. 
      int[] reversedArray = ReverseArrayIndexValue(targetOrder); 

      // Finally, sort the base array. 
      Array.Sort(reversedArray, peopleArray); 

      // Display names in order. 
      foreach (var item in peopleArray) 
      { 
       Console.WriteLine(item.Name); 
      } 

      Console.Read(); 
     } 

     /// <summary> 
     /// "Reverses" the indices and values of an array. 
     /// E.g.: [2, 0, 1] becomes [1, 2, 0]. 
     /// The value at index 0 is 2, so the value at index 2 is 0. 
     /// The value at index 1 is 0, so the value at index 0 is 1. 
     /// The value at index 2 is 1, so the value at index 1 is 2. 
     /// </summary> 
     /// <param name="target"></param> 
     /// <returns></returns> 
     private static int[] ReverseArrayIndexValue(int[] target) 
     { 
      int[] swappedArray = new int[target.Length]; 

      for (int i = 0; i < target.Length; i++) 
      { 
       swappedArray[i] = Array.FindIndex(target, t => t == i); 
      } 

      return swappedArray; 
     } 
    } 
} 
+0

Was? ist dein gewünschtes Endergebnis? Willst du nur eine 'Liste ' nach Ihren Kriterien sortiert? Ich bin ein wenig unklar mit all den Arrays, die Sie umwerfen, was nur ein Teil Ihrer Arbeitsversuche ist und was die Ausgabe erfordert ... – Chris

+0

Beide Gruppen 1 und 2 haben die nächstniedrigere Punktzahl. Welches Kriterium würde verwendet, um festzustellen, dass Gruppe 1 die nächste ist? –

+0

@Chris Mein gewünschtes Endergebnis ist das Array, mit dem ich in diesem Fall (peopleArray) begonnen habe, in sortierter Reihenfolge. – SortingAndOrdering

Antwort

2

Wie ich verstehe, wollen Sie die sortieren Eingabefeld an Ort und Stelle.

Erstens kann der Sortierabschnitt vereinfacht werden (und effiziente) durch erst OrderBy Score und dann GroupBy Gruppe, die definierte Verhaltens Enumerable.GroupBy Verwendung:

Der IGrouping < TKey, Telement> Objekte ergeben in einer Reihenfolge basierend auf der Reihenfolge der Elemente in der Quelle, die den ersten Schlüssel jedes IGrouping < TKey, TElement> erzeugt hat. Elemente in einer Gruppierung werden in der Reihenfolge zurückgegeben, in der sie in der Quelle angezeigt werden.

Sobald Sie, dass alles, was Sie brauchen, ist das Ergebnis zu glätten, iterieren es (damit es ausgeführt wird) und legte die ergab Gegenstände in ihrem neuen Ort:

var sorted = peopleArray 
    .OrderBy(e => e.Score) 
    .ThenBy(e => e.Group) // to meet your second requirement for equal Scores 
    .GroupBy(e => e.Group) 
    .SelectMany(g => g); 
int index = 0; 
foreach (var item in sorted) 
    peopleArray[index++] = item; 
+1

Das funktioniert perfekt. Vielen Dank. Meine Güte .. es ist so viel weniger Code als meine ursprüngliche Implementierung. – SortingAndOrdering

+1

Sie sind herzlich willkommen Kumpel, deshalb sind wir hier :) –

2

nicht sicher, ob ich wirklich verstanden, was das gewünschte Ergebnis sein sollte, aber dies zumindest gibt dieselbe Reihenfolge wie in den Kommentaren in Beispiel erwähnt:

var sortedNames = peopleArray 
      // group by group property 
      .GroupBy(x => x.Group) 
      // order groups by min score within the group 
      .OrderBy(x => x.Min(y => y.Score)) 
      // order by score within the group, then flatten the list 
      .SelectMany(x => x.OrderBy(y => y.Score)) 
      // doing this only to show that it is in right order 
      .Select(x => 
      { 
       Console.WriteLine(x.Name); 
       return false; 
      }).ToList(); 
+0

Gute Arbeit. Mein einziger Kommentar wäre, dass Sie in Ihrer Testauswahl 'return x' anstelle von' return false' geben sollten. Ansonsten erhältst du am Ende eine Liste von Booleschen Werten. Und während ich weiß, dass in Live-Code, der letzten Select nicht da sein wird, ist es noch schöner für eine Demo, so voll funktionsfähig wie möglich zu sein. – Chris

+0

Die LINQ-Sortierung funktioniert perfekt, aber gibt es eine Möglichkeit, die Sortierung auf das ursprüngliche Array anzuwenden? Ich möchte nicht mit einer sortierten Kopie davon enden, ich brauche das Original sortiert. – SortingAndOrdering

0

Wenn Ihr gewünschtes Ergebnis ist weniger Codezeile. Wie wäre es damit?

var peoples = peopleModel.OrderBy(i => i.Score).GroupBy(g => 
       g.Group).SelectMany(i => i, (i, j) => new { j.Name }); 

1) Bestellliste von Partituren

2) Gruppe durch

Gruppierung

3) Flatten die gruppierte Liste und neue Liste erstellen mit "Namen" Eigenschaft mit Select

Informationen mit anonymen Typ https://dzone.com/articles/selectmany-probably-the-most-p

0
int[] order = Enumerable.Range(0, peopleArray.Length) 
         .OrderBy(i => peopleArray[i].Score) 
         .GroupBy(i => peopleArray[i].Group) 
         .SelectMany(g => g).ToArray();   // { 2, 1, 3, 0, 4 } 

Array.Sort(order, peopleArray); 

Debug.Print(string.Join(", ", peopleArray.Select(p => p.ArrayIndex))); // "3, 1, 0, 2, 4" 
Verwandte Themen