LINQ:转换可枚举直到满足结束条件,如果满足结束条件则 return
LINQ: transform enumerable until end condition is met, and return if the end condition was met
我有一个可枚举的字符串strArr
。我想尽可能将所有条目转换为 int
,当条目不可转换为 int
时,枚举应该停止。我还想 return 一个 bool
告诉我是否可以转换所有条目。
所以我正在寻找的是 int.TryParse
的可枚举版本(如果转换成功,它 return 既是 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
布尔值或可枚举值的方法,但是如何?
我知道我可以比较 strArr
和 returned int 数组的长度,看看转换是否成功。但我想了解 LINQ 的用法,而不仅仅是解决手头的问题(我总是可以只做一些循环而根本不使用 LINQ)。
感谢 mjwills 的小提示,我明白了:
bool parseSuccess = true;
int[] intArr = strArr.
Select(s => { parseSuccess &= int.TryParse(s, out int i); return i; }).
TakeWhile(i => parseSuccess).ToArray();
应避免从 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 (thanks 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;
我有一个可枚举的字符串strArr
。我想尽可能将所有条目转换为 int
,当条目不可转换为 int
时,枚举应该停止。我还想 return 一个 bool
告诉我是否可以转换所有条目。
所以我正在寻找的是 int.TryParse
的可枚举版本(如果转换成功,它 return 既是 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
布尔值或可枚举值的方法,但是如何?
我知道我可以比较 strArr
和 returned int 数组的长度,看看转换是否成功。但我想了解 LINQ 的用法,而不仅仅是解决手头的问题(我总是可以只做一些循环而根本不使用 LINQ)。
感谢 mjwills 的小提示,我明白了:
bool parseSuccess = true;
int[] intArr = strArr.
Select(s => { parseSuccess &= int.TryParse(s, out int i); return i; }).
TakeWhile(i => parseSuccess).ToArray();
应避免从 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 (thanks 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;