LINQ:转换可枚举直到满足结束条件,并在满足结束条件时返回

时间:2018-04-19 12:28:30

标签: c# linq out

我有一个可枚举的字符串strArr。我想将所有条目转换为int,只要可能,并且当条目不能转换为int时,枚举应该停止。我还想返回bool,告诉我是否可以转换所有条目。

所以我要找的是int.TryParse的可枚举版本(如果转换成功并且转换后的值,则返回bool)。

我认为可以用LINQ完成,但我无法理解。以下是我的想法:

strArr.
    Select(s => { bool b = int.TryParse(s, out int i); return (i, b); }).
        // returns both the converted int value and the parsing bool for each entry
    TakeWhile(ib => ib.b).
        // will stop when the first parsing bool was false
    Select(ib => i).ToArray();
        // will only take the ints from the (int, bool) pairs and return an array

此解决方案会将转换后的条目作为数组提供给我,但它不会告诉我转换是否成功。我需要一种方法out bool或enumerable,但是怎么样?

我知道我可以比较strArr和返回的int数组的长度,看看转换是否成功。但是我想了解LINQ的用法,而不仅仅是解决手头的问题(我总是可以做一些循环,从不使用LINQ)。

2 个答案:

答案 0 :(得分:2)

感谢mjwills'我得到了一点暗示:

bool parseSuccess = true;
int[] intArr = strArr.
    Select(s => { parseSuccess &= int.TryParse(s, out int i); return i; }).
    TakeWhile(i => parseSuccess).ToArray();

答案 1 :(得分:1)

应该避免从lambda内部变量变量,因为它会导致相当脆弱的代码。

您可以创建自己的TakeWhilePlusOne方法,为您提供第一个未通过谓词的条目,如下所示:

static class EnumerableExt {
    public static IEnumerable<TSource> TakeWhilePlusOne<TSource>(
        this IEnumerable<TSource> source
    ,   Func<TSource,bool> predicate
    ) {
        if (source == null) throw new ArgumentNullException(nameof(source));
        if (predicate == null) throw new ArgumentNullException(nameof(predicate));
        return TakeWhilePlusOneIterator<TSource>(source, predicate);
    }

    static IEnumerable<TSource> TakeWhilePlusOneIterator<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate) {
        foreach (TSource element in source) {
            var stop = !predicate(element);
            yield return element;
            if (stop) {
                break;
            }
        }
    }
}

注意:此实现是TakeWhile in the reference source的修改。

现在您可以使用此方法实现以下目标:

var pairs = strArr
    .Select(s => new { ParseSuccess = int.TryParse(s, out int i), Value = i })
    .TakeWhilePlusOne(i => i.ParseSuccess)
    .ToArray();
var intArray = pairs.Where(p => p.ParseSuccess).Select(p => p.Value).ToArray();
var allValuesAreGood = pairs.LastOrDefault()?.ParseSuccess ?? true;

您还可以使用TakeUntil from morelinq library(感谢mjwills评论):

var pairs = strArr
    .Select(s => new { ParseSuccess = int.TryParse(s, out int i), Value = i })
    .TakeUntil(i => !i.ParseSuccess)
    .ToArray();
var intArray = pairs.Where(p => p.ParseSuccess).Select(p => p.Value).ToArray();
var allValuesAreGood = pairs.LastOrDefault()?.ParseSuccess ?? true;