Возможно ли это с помощью одного запроса LINQ?

#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");