Начинаю изучать C#, пишу личный проект в тренировочных целях, и столкнулся с такой ситуацией:
Имеется массив inputArray, содержащий 9 экземпляров некоего класса Cell. Одним из полей в Cell является массив из девяти boolean`ов PossibleValues. Требовалось найти в inputArray такие экземпляры Cell, где в PossibleValues есть всего два true и собрать эти Cell (если таковые будут) в новый массив. Я сделал это так:
Cell[] arr = inputArray.Where(x => x.PossibleValues.Where(k => k).Count() == 2).ToArray();
Далее нужно найти в arr такие пары Cell`ов, у которых PossibleValues будут полностью совпадать (т.е. оба true у них будут по одним и тем же индексам), заранее известно, что одинаковые наборы значений PossibleValues будут встречаться не более, чем по два раза.
При обнаружении такой пары, нужно запомнить индексы их true и скинуть PossibleValues по этим индексам в false всем Cell в inputArray, кроме тех, что мы только что нашли. Ожидается, что таких пар может быть от 0 до 3.
Есть способ компактно это записать с помощью LINQ?
Update:
Обратите внимание, что вывод осуществляется через изменение inputArray.PossibleValues, а не arr (arr это уже выборка тех Cell, где присутствуют только два true в PossibleValues). Класс Cell включает в себя не только PossibleValues, есть и другие поля, которые в задаче не важны. В одном из ответов я выложил свой вариант решения задачи.
Ответ
Вы можете легко описывать запросы к данным при помощи LINQ, но модификация данных при помощи LINQ не описывается так легко. В идеологии LINQ (как и у всего функционального программирования) предлагается не модифицировать данные, а создавать новые. Когда у вас нету модифицирующего кода, LINQ-запросы получаются легко, и функционируют так, как вы думаете. Если вы используете с LINQ код с побочными эффектами (например, изменяющий исходную последовательность), возможны «неприятности», если только вы не материализуете все LINQ-запросы заранее.
Ваша задача при помощи LINQ делается в два приёма: сначала вычисление нужных данных, а потом уж и модификация:
var result =
from l in arr
from r in arr
where l != r && l.PossibleValues.SequenceEqual(r.PossibleValues)
select new { set = new[] { l, r }, values = l.PossibleValues.ToArray() };
foreach (var task in result.ToList())
{
foreach (var cell in arr.Except(task.set))
{
for (int i = 0; i < task.values.Length; i++)
{
if (task.values[i])
cell.PossibleValues[i] = false;
}
}
}
Обновление: Ещё немного больше LINQ:
foreach (var task in result.ToList())
{
foreach (var cell in arr.Except(task.set))
cell.PossibleValues =
cell.PossibleValues.Zip(task.values, (cv, mask) => cv && !mask).ToArray();
}
Комментариев нет:
Отправить комментарий