2015-12-11 6 views
10

auf Bestellung Können sagen, ich ArtikelWie sortieren

items : [{id:1,...}, {id:2,...}, {id:3,...}] 

haben und Ordnung: [2, 3, 1] eine zählbare

items : [{id:2,...}, {id:3,...}, {id:1,...}] 

ich es erwarten bekommen etwas in den Zeilen von

items.Select(o => new {key = ordering[i++], value = o}) 
    .OrderBy(k => k.key) 
    .Select(o => o.value) 

aber gibt es eine sauberere Lösung?


Folge habe ich, dass die Arbeit (HimBromBeere, Domysee, qxg) verifiziert

var expectedOrder = ordering.Select(x => result.First(o => o.Id == x)); 
var expectedOrder = result.OrderBy(item => Array.FindIndex(ordering,i => i == item.Id)); 
var expectedOrder = result.OrderBy(item => ordering.ToList().FindIndex(i => i == item.Id)); 
var expectedOrder = 
    from o in ordering 
    join i in result 
    on o equals i.Id 
    select i; 

FWI, das für die Überprüfung Test war:

[Test] 
    [TestCase(1, 2, 3)] 
    [TestCase(1, 3, 2)] 
    [TestCase(2, 1, 3)] 
    [TestCase(2, 3, 1)] 
    [TestCase(3, 1, 2)] 
    public void Test_Should_Fail_If_GetMessages_Does_Not_Return_Sorted_By_Sent_Then_By_Id_Result(params int[] ordering) 
    { 
     var questions = GetQuestionsData();      
     Mock.Get(_questionService) 
      .Setup(o => o.GetQuestions()) 
      .Returns(questions); 
     var result = _mailboxService.GetMessages();  
     var expectedOrder = ordering.Select(x => result.First(o => o.Id == x)); 

     // Act 
     Action sortOrder =() => expectedOrder.Should() 
      .BeInDescendingOrder(o => o.Sent) 
      .And.BeInDescendingOrder(o => o.Id); 

     // Assert 
     sortOrder.ShouldThrow<AssertionException>(); 
    } 
+0

Es könnte besser sein, eine Verknüpfung zu verwenden, um den Code lesbarer zu machen.Auch könnte ein bisschen sicherer sein, da das i ++ nicht mehr benötigt wird. – momar

+0

Wie sauber willst du? Die aktuelle Lösung ist nicht schlecht. – qxg

+0

Nur um klar zu sein, ist 'bestellen' eine Liste von 1 basierten Indizes oder eine Liste von IDs in der Reihenfolge, die Sie wollen? Weil Sie eine Reihe von Antworten erhalten, die eine Liste der IDs annehmen (die mit dem Beispiel übereinstimmt), aber Ihr aktueller Code verwendet die ID nicht, um einen Wert in der 'Reihenfolge' zu ​​suchen. – juharr

Antwort

3

Etwas Ähnliches Ich denke, :

var result = ordering.Select(x => items.First(y => y.id == x.id)); 

Arbeitsbeispiel:

var items = new[] { new { id = 1, name = "1" }, new { id = 2, name = "2" }, new { id = 3, name = "3" }, new { id = 4, name = "4" } }; 
var result = new[] { 2, 3, 1 }.Select(x => items.First(y => y.id == x)); 

Diese filtert auch jene items aus, deren Index nicht in ordering enthalten.

+0

Das OP verwendet derzeit nicht die 'id', um den Bestellwert in 'ordering' zu suchen. Die Verwendung von "i ++" zeigt an, dass die "Ordnung" eine Liste von Indizes, nicht von IDs ist. Das Beispiel stimmt einfach damit überein, dass es eine Liste von IDs ist. – juharr

3

Sie könnten die Überlastung von Select verwenden, die Ihnen den Index

items.Select((o,i) => new {key = ordering[i+1], value = o}) 
    .OrderBy(k => k.key) 
    .Select(o => o.value); 

stattdessen gibt Dies wird besser sein, weil es das aufgenommene i Variable in Ihrem Beispiel eliminiert.

2

Wenn ordering ein List ist, können Sie das tun:

items.OrderBy(item => ordering.FindIndex(i => i == item.id)) 
+0

Während die IDs so zusammenpassen, vermute ich, dass 'ordern' eigentlich eine Liste von Indizes und nicht von IDs ist. – juharr

+0

@juharr oh gut, in diesem Fall wäre meine Lösung falsch. Mal sehen, ob OP das klarstellt. – Domysee

2

Die einzige Verbesserung ist, dass es nicht um braucht wieder oder neues Objekt, aber im Wesentlichen ist die gleiche wie Original-Code.

var query = from o in ordering 
      join i in items 
       on o equals i.Id 
      select i; 
3

Wenn Ihre IDs fortlaufend sind, können Sie das Order-Array für jeden Index neu anordnen - die Position der ID mit demselben Index im Ergebnis-Array. Dies kann so geschehen:

int[] order = new[] {2, 3, 1}; 

order = Enumerable.Range(1, order.Length) 
    .OrderBy(x => order[x - 1]) 
    .ToArray(); 

//Now order = { 3, 1, 2 } which are the one-based indices of each position in the original order array. 

Jetzt können Sie einfach Ihre Enumerable bestellen dieses Ergebnis Array:

items = items.OrderBy(x => order[x.Id - 1]); 

Beachten Sie, wenn Sie Ihre Bestellung Array auf diese Weise in erster Linie darstellen würde, die erste Linq wäre unnötig, und auf diese Weise Sie keine redundanten Wiederholungen oder sub- Linq s:

int[] order = new[] {3, 1, 2}; //1 is third, 2 is first, 3 is second. 

items = items.OrderBy(x => order[x.Id - 1]);