Страницы

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

суббота, 22 июня 2019 г.

Работа с массивами через LINQ в C#

Начинаю изучать 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(); }

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

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