2014-05-10 4 views
8

Я пытаюсь получить список упорядоченной строки, чтобы самые длинные были на обоих концах списка, а самые короткие - посередине. Например:LINQ нелинейный порядок по длине строки

A 
BB 
CCC 
DDDD 
EEEEE 
FFFFFF 

бы упорядочиваются как:

FFFFFF 
DDDD 
BB 
A 
CCC 
EEEEE 

EDIT: Чтобы уточнить, я специально искал реализации LINQ, чтобы достичь желаемых результатов, потому что я не был уверен, как/если это возможно, используя LINQ.

ответ

7

Не спрашивайте, как и почему ... ^^

list.Sort(); // In case the list is not already sorted. 

var length = list.Count; 

var result = Enumerable.Range(0, length) 
         .Select(i => length - 1 - 2 * i) 
         .Select(i => list[Math.Abs(i - (i >> 31))]) 
         .ToList(); 

Хорошо, прежде чем я забуду, как это работает, здесь вы идете.

Список с 6 пунктами, например, должен быть переупорядочен; самая длинная строка указана в индексе 5, самая короткая - в индексе 0 предварительно отсортированного списка.

5 3 1 0 2 4 

Мы начинаем с Enumerable.Range(0, length) получая

0 1 2 3 4 5 

затем применяем i => length - 1 - 2 * i получая

5 3 1 -1 -3 -5 

и у нас есть неотрицательная часть правильно. Теперь обратите внимание, что i >> 31 является арифметическим сдвигом влево и копирует бит знака во все биты. Поэтому неотрицательные числа дают 0, а отрицательные числа - -1. Это, в свою очередь, означает, вычитая i >> 31 не изменится неотрицательные числа, но добавить 1 к отрицательных чисел с получением

5 3 1 0 -2 -4 

и теперь мы, наконец, применить Math.Abs() и получить

5 3 1 0 2 4 

, который является желаемым результатом. Он работает аналогично для списков нечетной длины.

+0

Впечатляющий ответ. – chad

+0

Но вы ничего не хотите вводить в производственный код. Или, может быть, только с * лотом * комментариев. –

10

Вы можете создать две упорядоченные группы, а затем заказать первую группу нисходящую (уже сделано), а второй группы в порядке возрастания:

var strings = new List<string> { 
     "A", 
     "BB", 
     "CCC", 
     "DDDD", 
     "EEEEE", 
     "FFFFFF"}; 
var two = strings.OrderByDescending(str => str.Length) 
     .Select((str, index) => new { str, index }) 
     .GroupBy(x => x.index % 2) 
     .ToList(); // two groups, ToList to prevent double execution in following query 
List<string> ordered = two.First() 
    .Concat(two.Last().OrderBy(x => x.str.Length)) 
    .Select(x => x.str) 
    .ToList(); 

Результат:

[0] "FFFFFF" string 
[1] "DDDD"  string 
[2] "BB"  string 
[3] "A"   string 
[4] "CCC"  string 
[5] "EEEEE"  string 
0

Еще один вариант, который я нахожу более удобным для чтения и легко следовать: У вас есть упорядоченный список:

var strings = new List<string> { 
     "A", 
     "BB", 
     "CCC", 
     "DDDD", 
     "EEEEE", 
     "FFFFFF"}; 

Создать новый список и просто чередовать, где вы добавляете элементы ::

var new_list = new List<string>(); // This will hold your results 
bool start = true;     // Insert at head or tail 

foreach (var s in strings) 
{ 
    if (start) 
     new_list.Insert(0,s); 
    else   
     new_list.Add(s); 

    start = !start;    // Flip the insert location 
} 

Сладкие и просто :)

Что касается Daniel Брукнера комментарий, если вы заботитесь о том, какие строки приходят В первую очередь вы также можете изменить условия запуска на:

// This will make sure the longest strings is first 
bool start= strings.Count()%2 == 1; 
+0

Это будет отключение для списков четной длины - самая длинная строка будет в последней позиции. Fix: 'var start = strings.Count% 2 == 1;' –

+0

Он сказал, что 'longest находятся на обоих концах списка', так что все еще работает в моей книге :). Я хотел бы избежать вычисления, где положить строку, и KISS. – Noctis

+0

Справедливая точка. Я, наконец, отказался от выяснения того, как я буду реализовывать его в производственном коде. –

Смежные вопросы