Страницы

Поиск по вопросам

суббота, 6 июля 2019 г.

Расширение IEnumerable по маске

Кстати о Linq-однострочниках)
В общем, есть такая задача: Имеется IEnumerable, содержащий в себе некоторые строки. А также есть int[], содержащий в себе позиции, на которые в вышеуказанной коллекции нужно вставить пустые строки
Пример:
// IEnumerable collection: "Some" "Strings" "To" "Test" "Method"
// int[] mask: 1 2 4
// IEnumerable result: "Some" "" "" "Strings" "" "To" "Test" "Method"
Красота Linq в том, чтобы без каких-либо дополнительных переменных и явных циклов преобразовать коллекцию. Однако у меня без введения новой переменной решить задачу не получилось, так что хотел бы испросить у вас: какое решение будет более элегантным?)
Мои реализации:
В лоб:
int i = 0; List result = new List(); foreach (string x in collection) { while (mask.Contains(i)) { result.Add(string.Empty); ++i; } result.Add(x); ++i; }
То же, но через SelectMany
int i = 0; IEnumerable result = collection.SelectMany(x => { List part = new List(); while (mask.Contains(i)) { part.Add(string.Empty); ++i; } part.Add(x); ++i; return part; });
UPD:
Обычно это важно в подобного рода задачах, так что уточняю: mask является упорядоченным массивом


Ответ

Ещё одна вариация той же идеи с предварительной обработкой.
Создадим словарь:
var dict = mask.OrderBy(n => n) .Select((n, idx) => n - idx - 1) .GroupBy(n => n) .ToDictionary(g => g.Key, g => g.Count());
Имея это, получаем такой запрос:
Enumerable.Repeat("", dict.TryGetValue(-1, out var k) ? k : 0).Concat( collection.SelectMany((s, idx) => Enumerable.Repeat("", dict.TryGetValue(idx, out var t) ? t : 0) .Prepend(s)) );
Первая строка нужна для случая, когда в маске есть индекс 0.

Комментариев нет:

Отправить комментарий