LINQ Max() с нулями
У меня есть список, содержащий пучок точек (с компонентом X и Y).
Я хочу получить Max X для всех точек в списке, например:
double max = pointList.Max(p=> p.X);
Проблема заключается в том, что у меня есть нуль в списке вместо точки. Каким будет лучший способ обойти эту проблему?
Ответы
Ответ 1
Ну, вы можете просто отфильтровать их:
pointList.Where(p => p != null).Max(p => p.X)
С другой стороны, если вы хотите, чтобы null
обрабатывался так, как если бы они были точками, имеющими X-координату 0
(или аналогичные), вы могли бы сделать:
pointList.Max(p => p == null ? 0 : p.X)
Обратите внимание, что оба метода будут метаться, если последовательность пуста. Одним из способов решения этой проблемы (если желательно) было бы следующее:
pointList.DefaultIfEmpty().Max(p => p == null ? 0 : p.X)
Ответ 2
Если вы хотите указать значение по умолчанию для X нулевой точки:
pointList.Max(p => p == null ? 0 : p.X)
Или предоставить значение по умолчанию для пустого списка:
int max = points.Where(p => p != null)
.Select(p => p.X)
.DefaultIfEmpty()
.Max();
Ответ 3
Я бы не рекомендовал использовать DefaultIfEmpty
в этом случае, так как он создает довольно большой SQL по сравнению с другими альтернативами.
Пожалуйста, посмотрите на этот пример:
У нас есть список модулей для страницы и мы хотим получить максимальное значение столбца "Сортировка". Если в списке нет записей, возвращается null. DefaultIfEmpty
проверяет значение null и возвращает значение по умолчанию для типа данных столбца, когда столбец имеет значение null.
var max = db.PageModules.Where(t => t.PageId == id).Select(t => t.Sort).DefaultIfEmpty().Max();
В результате создается следующий SQL:
exec sp_executesql N'SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
MAX([Join1].[A1]) AS [A1]
FROM ( SELECT
CASE WHEN ([Project1].[C1] IS NULL) THEN 0 ELSE [Project1].[Sort] END AS [A1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN (SELECT
[Extent1].[Sort] AS [Sort],
cast(1 as tinyint) AS [C1]
FROM [dbo].[PageModules] AS [Extent1]
WHERE [Extent1].[PageId] = @p__linq__0 ) AS [Project1] ON 1 = 1
) AS [Join1]
) AS [GroupBy1]',N'@p__linq__0 int',@p__linq__0=11
go
Если вместо этого вместо столбца укажем значение nullable и пусть Convert.ToInt32()
обрабатывает значение null так:
var max = Convert.ToInt32(db.PageModules.Where(t => t.PageId == id).Max(t => (int?)t.Sort));
Затем мы получаем следующий SQL:
exec sp_executesql N'SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
MAX([Extent1].[Sort]) AS [A1]
FROM [dbo].[PageModules] AS [Extent1]
WHERE [Extent1].[PageId] = @p__linq__0
) AS [GroupBy1]',N'@p__linq__0 int',@p__linq__0=11
go
Я действительно рекомендую использовать ExpressProfiler для проверки SQL, который выполняется:
http://expressprofiler.codeplex.com/
Последнее выражение Linq также может быть записано как:
var max = Convert.ToInt32(db.PageModules.Where(t => t.PageId == id).Select(t => (int?)t.Sort).Max());
и будет выдавать тот же SQL, но мне нравится более краткий .Max(t => (int?)t.Sort)
.
Ответ 4
double max = pointList.Where(p=>p != null).Max(p=>p.X)
Должен работать.
Ответ 5
Попробуйте выполнить кастинг с нулевым значением
double max = (double?)pointList.Max(p => p.X);
больше:
Макс. или по умолчанию?
Ответ 6
Почему бы просто:
double? maxOrNull = pointList.
.Where(p => p != null)
.OrderByDescending(p => p.x)
.FirstOrDefault();
double max = 0;
if (maxOrNull.HasValue) max = maxOrNull.Value;
Это будет работать в списках памяти и Linq2Sql и, вероятно, также эффективно.
Ответ 7
Поместите элемент с нулевым значением в INSIDE, чтобы убедиться, что пустым списком будет присвоено значение null. Затем вы можете добавить значения по умолчанию.
double max = pointList.Max(p=>(double?)p.X) ?? 0;