2015-07-30 2 views
20

. Меня недавно попросил сотрудник: возможно ли просто взять первые пять элементов и последние пять элементов по одному запросу из массив?Возьмите первые пять элементов и последние пять элементов из массива одним запросом, используя LINQ

int[] someArray = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; 

Что я пробовал:

int[] someArray = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; 
var firstFiveResults = someArray.Take(5); 
var lastFiveResults = someArray.Skip(someArray.Count() - 5).Take(5); 
var result = firstFiveResults; 
result = result.Concat(lastFiveResults); 

Можно ли просто взять первые пять элементов, а последние пять элементов от одного запроса?

+2

Вы попробовали var result = someArray.Take (5) .Union (someArray.Skip (someArray.Count() - 5) .Take (5)); ? –

+0

как насчет 'someArray.Take (5) .Concat (someArray.Reverse(). Take (5));'? – Nilesh

+0

@MitatKoyuncu, 'Union' удаляет повторяющиеся записи, которые могут быть или не быть такими, какие вы хотите. Если вы определенно хотите получить 10 результатов, используйте 'Concat' –

ответ

28

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

int[] someArray = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; 

int[] newArray = someArray.Where((e, i) => i < 5 || i > someArray.Length - 6).ToArray(); 

foreach (var item in newArray) Console.WriteLine(item); 

Выход:

0, 1, 2, 3, 4, 14, 15, 16, 17, 18 
+3

Этот ответ является одним из тех, которые не выбрасывают, если входная последовательность содержит менее 5 элементов. Однако OP никогда не указывал поведение для этого случая - то есть, если дублирующая головка и хвост дублируются в выходе? –

+0

Учитывая, что Enumerable.Take не выбрасывает, если в последовательности меньше элементов, чем указано, я бы подумал, что решение @ Fabjan имеет «правильное поведение», если не указано иное. –

+0

В случае, когда 'someArray' имеет длину в интервале 5..9, это решение не дает того же результата, что и мои решения (см. Ниже). Может ли аскер гарантировать, что массив всегда достаточно длинный? В случае, когда длина массива меньше 5, мои решения вызывают исключение, тогда как вышеприведенное решение дает все записи один раз. –

2

Это должно работать

someArray.Take(5).Concat(someArray.Skip(someArray.Count() - 5)).Take(5); 
+3

Где это отличается от того, что написал OP? – HimBromBeere

+0

Это довольно неэффективно. Вызов 'Count()' выполняет полное перечисление источника, а 'Skip' выполняет большую работу. Если функция должна работать в 'IEnumerable ', было бы лучше всего перечислить источник только один раз. –

+1

@Drew Источником в этом случае является массив фиксированного размера, поэтому я бы предположил, что 'Count()' использует перегруз, чтобы просто вернуть размер массива. – Peter

11

Решение с ArraySegment<> (требует .NET 4.5 (2012) или более поздней версии):

var result = new ArraySegment<int>(someArray, 0, 5) 
    .Concat(new ArraySegment<int>(someArray, someArray.Length - 5, 5)); 

И решение с Enumerable.Range:

var result = Enumerable.Range(0, 5).Concat(Enumerable.Range(someArray.Length - 5, 5)) 
    .Select(idx => someArray[idx]); 

Оба этих решения избегают повторения через «средний» массив (индексы с 5 по 13).

2

Попробуйте это:

int[] someArray = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }; 
var firstFiveResults = someArray.Take(5); 
var lastFiveResults = someArray.Reverse().Take(5).Reverse(); 
var result = firstFiveResults; 
result = result.Concat(lastFiveResults); 

второй обратный() переупорядочивает номера, поэтому вы не получите 18,17,16,15,14

+0

Два 'Reverse()' будут еще более неэффективными, чем 'Skip (...)'. –

+3

@JeppeStigNielsen - это полностью зависит от реализации. – NPSF3000

+1

@ NPSF3000 Конечно. Но это меня очень удивляет: 'someArray.Reverse(). Take (5) .Reverse(). ToArray()' на самом деле _faster_, чем 'someArray.Skip (someArray.Length-5) .ToArray()' для огромных массивов. Я проверил источник, и выясняется, что 'Skip' не делает оптимизаций, когда переданный' IEnumerable 'имеет дополнительную структуру, поэтому он выполняет итерацию медленным способом. В то время как 'Reverse' создает внутренний struct' Buffer ', конструктор экземпляра которого использует оптимизацию' CopyTo (T [], int) ', когда он определяет, что источник реализует' ICollection '. Поэтому я ошибался в отношении реальной реализации! –

1

Пожалуйста, попробуйте следующее:

var result = someArray.Take(5).Union(someArray.Skip(someArray.Count() - 5).Take(5)); 
+0

'Союз' будет выбрасывать дубликаты. Вопрос не указывает на то, что поведение желательно. *** Редактирование: *** Итак, что вы ожидаете с помощью 'int [] someArray = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 , 0,0,0,0,}; –

+1

@JeppeStigNielsen Вы правы. Если массив имеет повторяющуюся запись, он работает неправильно. –

4

Попробуйте это:

var result = someArray.Where((a, i) => i < 5 || i >= someArray.Length - 5); 
+2

Это дубликат ответа Фабьяна и менее обширный. Однако использование '> =', так что 5 соответствует фактическому количеству принятых элементов, также я бы заменил 'a' на' _', чтобы указать неиспользуемую переменную. – Dorus

6

В случае, если вы не играете код головоломки с вашими коллегами, а просто хочет, чтобы создать новый массив с вашими критериями , Я бы не делал этого с запросами вообще, но использовал Array.copy.

Есть три различных случая:

  • исходный массив имеет меньше чем 5 пунктов
  • исходный массив имеет 5 до 9 пунктов
  • исходный массив имеет 10 или больше деталей

Третий - это простой случай, поскольку первый и последний 5 элементов различны и четко определены.

Остальные два требуют больше размышлений.Я собираюсь принять вы хотите следующее: проверьте эти предположения:

Если исходный массив имеет менее 5 элементов, вам понадобится массив из элементов 2 * (длина массива), например [ 1, 2, 3] становится [1, 2, 3, 1, 2, 3]

Если исходный массив имеет от 5 до 9 элементов, вам понадобится массив из примерно 10 элементов, например [ 1, 2, 3, 4, 5, 6] становится [1, 2, 3, 4, 5, 2, 3, 4, 5, 6]

демонстрация программа

public static void Main() 
{ 
    Console.WriteLine(String.Join(", ", headandtail(new int[]{1, 2, 3}))); 
    Console.WriteLine(String.Join(", ", headandtail(new int[]{1, 2, 3, 4, 5, 6}))); 
    Console.WriteLine(String.Join(", ", headandtail(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}))); 
} 

private static T[] headandtail<T>(T[] src) { 
    int runlen = Math.Min(src.Length, 5); 
    T[] result = new T[2 * runlen]; 
    Array.Copy(src, 0, result, 0, runlen); 
    Array.Copy(src, src.Length - runlen, result, result.Length - runlen, runlen); 
    return result; 
} 

, который работает в O (1);

Если вы : играйте в пазлы с вашими коллегами, ну и все самое интересное в головоломке, не так ли?

Это тривиально.

src.Take(5).Concat(src.Reverse().Take(5).Reverse()).ToArray(); 

Это работает в O (n).

+1

Спасибо. Это превосходно. Однако основным критерием является одна строка кода запроса, а не скорость выполнения. Тем не менее, я очень ценю ваш удивительный алгоритм! :) возьмите один балл от меня! – StepUp

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