#c# #linq #ienumerable
#c# #linq #ienumerable
Вопрос:
Предположим, у меня есть заданный объект типа IEnumerable<string>
, который является возвращаемым значением метода SomeMethod()
и который не содержит повторяющихся элементов. Я хотел бы иметь возможность «заархивировать» следующие строки в одном запросе LINQ:
IEnumerable<string> someList = SomeMethod();
if (someList.Contains(givenString))
{
return (someList.Where(givenString));
}
else
{
return (someList);
}
Редактировать: я ошибочно использовал Single
вместо First
. Теперь исправлено.
Я знаю, что могу «сжать» это с помощью троичного оператора, но дело не в этом. Я бы просто перечислил, чтобы иметь возможность достичь этого с помощью одной строки. Возможно ли это?
Комментарии:
1. Каков тип возвращаемого значения этого метода?
2. Возвращаемый тип
IEnumerable<string>
.3. Вы возвращаете как IEnumerable, так и одну строку.
4. Что вы хотите
someList.Single(givenString)
этим сказать?5. Мои извинения. Я действительно хотел использовать
Where(givenString)
вместоSingle(givenString)
.
Ответ №1:
Это вернет элементы с заданной строкой или все элементы, если given отсутствует в списке:
someList.Where(i => i == givenString || !someList.Contains(givenString))
Комментарии:
1. Не вызывает ли это. Содержит много раз, если есть элементы, которые не являются заданной строкой?
Ответ №2:
Природа вашего желаемого результата требует, чтобы вы либо сделали два запроса данных, как сейчас, либо буферизовали несоответствия, чтобы вернуть их, если совпадения не найдены. Последнее было бы особенно полезно в тех случаях, когда фактическое получение данных является относительно дорогостоящим вызовом (например, запрос к базе данных или служба WCF). Метод буферизации будет выглядеть следующим образом:
static IEnumerable<T> AllIfNone<T>(this IEnumerable<T> source,
Func<T, bool> predicate)
{
//argument checking ignored for sample purposes
var buffer = new List<T>();
bool foundFirst = false;
foreach (var item in source)
{
if (predicate(item))
{
foundFirst = true;
yield return item;
}
else if (!foundFirst)
{
buffer.Add(item);
}
}
if (!foundFirst)
{
foreach (var item in buffer)
{
yield return item;
}
}
}
Лень этого метода Where
ToList
зависит от того, содержит ли коллекция совпадение или нет. Если это произойдет, вы должны получить выполнение, аналогичное Where
. Если нет, вы получите примерно выполнение вызова ToList
(с накладными расходами на все неудачные проверки фильтра) и повторение результата.
Ответ №3:
Что не так с троичным оператором?
someList.Any(s => s == givenString) ? someList.Where(s => s == givenString) : someList;
Было бы лучше сделать Where, за которым следует Any, но я не могу придумать, как это сделать в одну строку.
var reducedEnumerable = someList.Where(s => s == givenString);
return reducedEnumerable.Any() ? reducedEnumerable : someList;
Ответ №4:
Невозможно изменить возвращаемый тип в методе, о чем вы спрашиваете. Первое условие возвращает строку, а второе условие возвращает набор строк.
Просто верните IEnumerable<string>
коллекцию и вызовите Single
возвращаемое значение следующим образом:
string test = ReturnCollectionOfStrings().Single(x => x == "test");