#c# #linq
#c# #linq
Вопрос:
Я хотел бы знать, возможно ли создать «однострочный» Linq для получения самого длинного строкового значения определенного столбца данных, что означает, что все данные столбца (числа, даты, строки …) должны быть преобразованы в строку, а затем возвращать самую длинную строку.
Я нашел только то, как получить самую длинную строку из списка или значение максимальной длины.
Это то, что я пробовал до сих пор (ошибка в длине):
string maxString = dt
.AsEnumerable()
.Select(row => row[mycolumn].ToString())
.Where(s => s.OrderByDescending(st => st.Length).First());
Комментарии:
1. «Это то, что я пробовал до сих пор (очевидно, не работает)» > Не так очевидно, пожалуйста, объясните.
2. @PatrickHofman, извините, этот код возвращает мне «символ не содержит определения длины»
3. Нет необходимости использовать
Where()
. Это должно сработать:.Select(row => row[mycolumn].ToString()).OrderByDescending(st => st.Length).First();
4. @StephanBauer, это было быстро. Спасибо, я знал, что это должно быть что-то простое 🙂
5. Если вы загружаете эту datatable из базы данных, вам, вероятно, следует подумать о написании SQL для этого. Загрузка всей таблицы в память и обработка ее с помощью Linq, вероятно, намного медленнее.
Ответ №1:
Вы ищете ArgMax
— значение такое, чтобы оно имело максимальное значение какого-либо свойства. Стандартный Linq не предоставляет ArgMax
, но вы можете реализовать его с помощью Aggregate
(т. Е. Получить одно значение из последовательности):
string maxString = dt
.AsEnumerable()
.Select(row => row[mycolumn].ToString())
.Aggregate((s, a) => a.Length > s.Length ? a : s);
Комментарии:
1. Это решение, вероятно, намного быстрее, чем принятый ответ, поскольку ему не нужно фактически сортировать список, просто повторите его один раз.
2. @Marie да, я тестировал, это самый быстрый. Хотя она отличается на 1 секунду по сравнению с ответом Гилада Грина в моем тесте на экспорт данных в Excel с помощью OpenXML для 320 тыс. строк и 7 столбцов.
Ответ №2:
Вы почти на месте:
string maxString = dt.AsEnumerable()
.Select(row => row[mycolumn].ToString())
.OrderByDescending(st => st.Length).FirstOrDefault();
A Where
ожидает предикат (функция, которая вернет true или false). Вместо этого просто упорядочите проекцию (the .Select
), как вы это делали, и извлеките первый элемент.
Обратите внимание, что это O(nlogn)
решение, которое можно улучшить до O(n)
решения, не сортируя, а находя элемент с максимальной длиной. Одним из возможных способов сделать это является ответ Дмитрия. Для менее чем огромных коллекций я не уверен, что кто-то действительно почувствует разницу, но это действительно стоит заметить.
Обратите внимание, что вы также можете использовать дополнительные .MaxBy
ссылки, которые можно добавить через Nuget (для репозитория GitHub), что даст вам O(n)
как производительность, так и желаемый «однострочный»:
var row = dt.AsEnumerable().MaxBy(r => r[mycolumn].ToString().Length);
Комментарии:
1. Разве сортировка для получения максимального значения не является эффективной? Или LINQ оптимизирует это?
2. @RoadRunner — вы правы, что сортировка будет менее эффективной. Это решение O (nlogn), в то время как можно сделать O (n), как в ответе ниже. Я старался держаться как можно ближе к оригиналу
3. ИМО, вы не должны рекомендовать FirstOrDefault, вы должны сначала рекомендовать проверку работоспособности. Тогда вам не нужно беспокоиться о появлении null.
Ответ №3:
Прежде всего, не используйте AsEnumerable
сразу после dt.
. Напишите как-нибудь так:
dt.OrderByDescending(row => row[mycolumn].Length).First();
Комментарии:
1. AsEnumerable не следует использовать, поскольку он переносит вычисления в память приложения, когда мой пример заставляет его выполнять вычисления на sql server. Когда все вычисления выполнены, функция First() передает результат в память приложения. Попробуйте мой пример и скажите мне, работает ли он, хорошо?
2. @Lucy82, ну, я проверил свой пример, и он отлично работает :). Но, как заметили люди выше, было бы намного лучше найти максимальное значение с помощью функции агрегирования. Для сортировки OrderBy требуется всего 1 цикл.
3. @Lucy82 Ни принятый, ни ответ Дмитрия не подходят для больших наборов данных, которые сохраняются в базе данных. Т. Е., если dt является таблицей в базе данных и содержит тысячи строк, другие ответы сначала извлекут все эти строки, а затем выполнят сортировку, чтобы получить максимальное значение.
4. @NoImagination Я могу ошибаться, но вопрос не в помеченной сущности или чем-то еще. Разве «когда мой пример заставляет его выполнять вычисления на sql server» не верно только в том случае, если они используют ORM, который поддерживает эту функциональность?
5. @NoImagination Само собой разумеется, что «dt» означает DataTable (эта
row[column]
часть также не имеет никакого смысла для запроса EF), что означает, что отключение AsEnumerable просто не сработает. Это также означает, что данные уже находятся в памяти, поэтому там действительно ничего не потеряно.