2015-12-11 4 views
10

Допустим, у меня есть элементыКак отсортировать на основе заказа

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

и имеют заказ: [2, 3, 1] получить перечислимого

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

Я ожидаю, что это будет что-то в строках

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

но есть ли более чистое решение?


Далее я проверил эту работу (HimBromBeere, Domysee, qxg)

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, это было для проверочного испытания:

[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

Возможно, лучше использовать соединение, чтобы сделать код более читаемым.Также может быть немного безопаснее, так как i ++ больше не требуется. – momar

+0

Насколько вы чисты? Нынешнее решение неплохое. – qxg

+0

Просто, чтобы быть понятным, «упорядочивает» список из 1 основанных индексов или список идентификаторов в том порядке, в котором вы хотите? Поскольку вы получаете кучу ответов, которые принимают свой список идентификаторов (которые соответствуют этому примеру), но вы используете текущий код, который фактически не использует идентификатор для поиска значения в 'ordering'. – juharr

ответ

3

Нечто подобное я думаю :

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

Рабочий пример:

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

Это также фильтрует те items аут, индекс которого не содержится в ordering.

+0

В настоящее время OP не использует 'id' для поиска значения заказа в' ordering'. Использование 'i ++' указывает, что 'ordering' является списком индексов, а не идентификаторами. Пример просто соответствует тому, что он является списком идентификаторов. – juharr

3

Вы можете использовать перегрузку Select, что дает вам индекс вместо

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

Это будет лучше, потому что он устраняет захваченное i переменную в вашем примере.

2

Если ordering является List, вы можете сделать это:

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

Хотя идентификаторы действительно совпадают, я предполагаю, что 'ordering' на самом деле является списком индексов, а не идентификаторов. – juharr

+0

@juharr oh хорошо, в этом случае мое решение было бы неправильным. Давайте посмотрим, разъяснит ли это OP. – Domysee

2

Единственное улучшение в том, что он не нужен заказ снова или новый объект, но, по существу такой же, как исходный код.

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

Если ваши идентификаторы подряд вы можете изменить массив порядка содержать для каждого индекса - положение идентификатора с одинаковым индексом в массиве результатов. Это можно сделать так:

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. 

Теперь вы можете заказать свой Enumerable с помощью этого результата массив:

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

Обратите внимание, что если бы вы представить свой массив заказа таким образом, в первую очередь, первый Linq было бы ненужным, и таким образом у вас нет лишних итераций или суб- 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]);