如何一次选择两个项目
本文关键字:两个 项目 何一次 选择 | 更新日期: 2023-09-27 18:20:00
我正在使用Regex.Split
编写PascalCaseParser,我希望一次从集合中选择两个项目。
此示例代码演示。
void Main()
{
string pascalCasedString = "JustLikeYouAndMe";
var words = WordsFromPascalCasedString(pascalCasedString);
words.Dump();
}
IEnumerable<string> WordsFromPascalCasedString(string pascalCasedString)
{
var rx = new Regex("([A-Z])");
return rx.Split(pascalCasedString)
.Where(c => !string.IsNullOrEmpty(c))
// how to select 2 elements at a time?
;
}
上述代码的结果是:
IEnumerable<String> (10 items)
J
ust
L
ike
Y
ou
A
nd
M
e
集合的每两个元素生成一个结果,我希望函数WordsFromPascalCasedString
生成该结果。
我的问题是:一般来说,你会如何处理一次退回两件物品的要求。我很好奇是否有任何有趣的非暴力方法。
就我个人而言,在这种特殊情况下,我会选择Simon Belanger的答案。但一般来说,要从IEnumerable
中选择连续的配对,您可以使用以下内容:
IEnumerable<Tuple<string, string>> WordsFromPascalCasedString(string pascalCasedString)
{
var rx = new Regex("([A-Z])");
var array = rx.Split(pascalCasedString)
.Where(c => !string.IsNullOrEmpty(c))
.ToArray();
var items = Enumerable.Range(0, array.Length / 2)
.Select(i => Tuple.Create(array[i * 2], array[i * 2 + 1]);
}
或者这需要更多的努力,但它是可重复使用的,更高效:
IEnumerable<Tuple<T, T>> Pairs<T>(IEnumerable<T> input)
{
var array = new T[2];
int i = 0;
foreach(var x in input)
{
array[i] = x;
i = (i + 1) % 2;
if (i == 0)
{
yield return Tuple.Create(array[0], array[1]);
}
}
}
IEnumerable<Tuple<string, string>> WordsFromPascalCasedString(string pascalCasedString)
{
var rx = new Regex("([A-Z])");
var output = rx.Split(pascalCasedString)
.Where(c => !string.IsNullOrEmpty(c));
var items = Pairs(output);
}
它可以很容易地扩展到n
:的组
IEnumerable<IEnumerable<T>> Batches<T>(IEnumerable<T> input, int n)
{
var array = new T[n];
int i = 0;
foreach(var x in input)
{
array[i] = x;
i = (i + 1) % n;
if (i == 0)
{
yield return array.ToArray();
}
}
if (i != 0)
{
yield return array.Take(i);
}
}
CCD_ 5中也存在类似的方法。
正则表达式应该是([A-Z][a-z]*)
。如果要同时包含数字,请调整最后一部分。如果希望在大写分隔符之后至少有一个小写元素,请使用+
而不是*
。
编辑对于实际问题,您需要在for
循环中具体化和迭代以获得更好的性能(传递列表一次)。在您的特定问题中,您可以使用Regex.Matches
var result = Regex.Matches("([A-Z][a-z]*)([A-Z][a-z]*)?", "AbCdEfGhIj")
.OfType<Match>()
.Where(m => m.Success)
.Select(m => Tuple.Create(m.Groups[1].Value, m.Groups[2].Value));
这只是为了分享,我在受到其他答案的启发后提出了解决方案。它并不比其他的好。。。
void Main()
{
string pascalCasedString = "JustLikeYouAndMe";
var words = WordsFromPascalCasedString(pascalCasedString);
words.Dump();
}
IEnumerable<string> WordsFromPascalCasedString(string pascalCasedString)
{
var rx = new Regex("([A-Z])");
return rx.Split(pascalCasedString)
.Where(c => !string.IsNullOrEmpty(c))
.InPieces(2)
.Select(c => c.ElementAt(0) + c.ElementAt(1));
}
static class Ext
{
public static IEnumerable<IEnumerable<T>> InPieces<T>(this IEnumerable<T> seq, int len)
{
if(!seq.Any())
yield break;
yield return seq.Take(len);
foreach (var element in InPieces(seq.Skip(len), len))
yield return element;
}
}
最简单的方法是编写只返回对的函数。
类似于:
IEnumerable<Tuple<T,T>> Pairs<T>(IEnumerable<T> items)
{
T first = default(T);
bool hasFirst = false;
foreach(T item in items)
{
if (hasFirst)
yield return Tuple.Create(first, item);
else
first = item;
hasFirst = !hasFirst;
}
}
CCD_ 11可能只是一种单线方法。这纯粹是娱乐代码,因为在某种方式上创建了大量垃圾,但没有使用可变对象。
IEnumerable<Tuple<T,T>> Pairs<T>(IEnumerable<T> collection)
{
return collection
.Aggregate(
Tuple.Create(false, default(T), Enumerable.Empty<Tuple<T,T>>()),
(accumulate, item)=> !accumulate.Item1 ?
Tuple.Create(true, item, accumulate.Item3) :
Tuple.Create(false, default(T),
accumulate.Item3.Concat(
Enumerable.Repeat(Tuple.Create(accumulate.Item2, item), 1))),
accumulate => accumulate.Item3);
}
奇数和偶数元素的Zip
(index %2 ==/!= 0
)是2线方法。请注意,迭代源集合两次。
IEnumerable<Tuple<T,T>> Pairs<T>(IEnumerable<T> collection)
{
return collection
.Where((item, index)=>index %2 == 0)
.Zip(collection.Where((item, index)=>index %2 != 0),
(first,second)=> Tuple.Create(first,second));
}