让我们假设我们有以下数组
var arr = new string[] {"foo","bar","jar","\r","a","b,"c","\r","x","y","z","\r");
还要忽略这是字符串的事实,所以请不要使用字符串黑客解决方案。
我想按序列中的每个“\ r”对这些元素进行分组。 也就是说,我想要一个数组/可枚举“foo”,“bar”,“jar”和另一个“a”,“b”,“c”等。
在可以使用的扩展名中是否有任何内容可以让我这样做,或者我必须按方法滚动我自己的小组吗?
答案 0 :(得分:4)
我为此目的编写了一个扩展方法,适用于任何IEnumerable<T>
。
/// <summary>
/// Splits the specified IEnumerable at every element that satisfies a
/// specified predicate and returns a collection containing each sequence
/// of elements in between each pair of such elements. The elements
/// satisfying the predicate are not included.
/// </summary>
/// <param name="splitWhat">The collection to be split.</param>
/// <param name="splitWhere">A predicate that determines which elements
/// constitute the separators.</param>
/// <returns>A collection containing the individual pieces taken from the
/// original collection.</returns>
public static IEnumerable<IEnumerable<T>> Split<T>(
this IEnumerable<T> splitWhat, Func<T, bool> splitWhere)
{
if (splitWhat == null)
throw new ArgumentNullException("splitWhat");
if (splitWhere == null)
throw new ArgumentNullException("splitWhere");
return splitIterator(splitWhat, splitWhere);
}
private static IEnumerable<IEnumerable<T>> splitIterator<T>(
IEnumerable<T> splitWhat, Func<T, bool> splitWhere)
{
int prevIndex = 0;
foreach (var index in splitWhat
.Select((elem, ind) => new { e = elem, i = ind })
.Where(x => splitWhere(x.e)))
{
yield return splitWhat.Skip(prevIndex).Take(index.i - prevIndex);
prevIndex = index.i + 1;
}
yield return splitWhat.Skip(prevIndex);
}
例如,在您的情况下,您可以像这样使用它:
var arr = new string[] { "foo", "bar", "jar", "\r", "a", "b", "c", "\r", "x", "y", "z", "\r" };
var results = arr.Split(elem => elem == "\r");
foreach (var result in results)
Console.WriteLine(string.Join(", ", result));
这将打印:
foo, bar, jar
a, b, c
x, y, z
(最后包括一个空行,因为收藏品末尾有"\r"
。
答案 1 :(得分:1)
如果您想使用标准IEnumerable
扩展方法,则必须使用Aggregate
(但这不像Timwi的解决方案那样可重复使用):
var list = new[] { "foo","bar","jar","\r","a","b","c","\r","x","y","z","\r" };
var res = list.Aggregate(new List<List<string>>(),
(l, s) =>
{
if (s == "\r")
{
l.Add(new List<string>());
}
else
{
if (!l.Any())
{
l.Add(new List<string>());
}
l.Last().Add(s);
}
return l;
});
答案 2 :(得分:0)
也请参阅此nest yields to return IEnumerable<IEnumerable<T>> with lazy evaluation。您可以使用SplitBy
扩展方法接受要拆分的谓词:
public static IEnumerable<IList<T>> SplitBy<T>(this IEnumerable<T> source,
Func<T, bool> separatorPredicate,
bool includeEmptyEntries = false,
bool includeSeparators = false)
{
var l = new List<T>();
foreach (var x in source)
{
if (!separatorPredicate(x))
l.Add(x);
else
{
if (includeEmptyEntries || l.Count != 0)
{
if (includeSeparators)
l.Add(x);
yield return l;
}
l = new List<T>();
}
}
if (l.Count != 0)
yield return l;
}
所以在你的情况下:
var arr = new string[] {"foo","bar","jar","\r","a","b,"c","\r","x","y","z","\r");
foreach (var items in arr.SplitBy(x => x == "\r"))
foreach (var item in items)
{
}
与Timwi相同,实施方式不同。没有错误检查,这就是你。由于您只遍历列表一次,因此速度会更快。