迭代IEnumerable,特殊大小写最后一个元素
本文关键字:大小写 最后一个 元素 IEnumerable 迭代 | 更新日期: 2023-09-27 18:12:57
我正在构建一个基于IEnumerable
的字符串,并做这样的事情:
public string BuildString()
{
var enumerable = GetEnumerableFromSomewhere(); // actually an in parameter,
// but this way you don't have to care
// about the type :)
var interestingParts = enumerable.Select(v => v.TheInterestingStuff).ToArray();
stringBuilder.Append("This is it: ");
foreach(var part in interestingParts)
{
stringBuilder.AppendPart(part);
if (part != interestingParts.Last())
{
stringBuilder.Append(", ");
}
}
}
private static void AppendPart(this StringBuilder stringBuilder, InterestingPart part)
{
stringBuilder.Append("[");
stringBuilder.Append(part.Something");
stringBuilder.Append("]");
if (someCondition(part))
{
// this is in reality done in another extension method,
// similar to the else clause
stringBuilder.Append(" = @");
stringBuilder.Append(part.SomethingElse");
}
else
{
// this is also an extension method, similar to this one
// it casts the part to an IEnumerable, and iterates over
// it in much the same way as the outer method.
stringBuilder.AppendInFilter(part);
}
}
我对这个习语不是很满意,但是我正在努力表达更简洁的东西。
这当然是更大的字符串构建操作的一部分(其中有几个类似于这个的块,以及介于两者之间的其他东西)-否则我可能会放弃StringBuilder
并直接使用string.Join(", ", ...)
。
我最接近于简化上面的方法是为每个迭代器构建这样的结构:
stringBuilder.Append(string.Join(", ", propertyNames.Select(prop => "[" + prop + "]")));
但是这里我仍然用+
连接字符串,这让人感觉StringBuilder
并没有真正贡献很多。
我如何简化这段代码,同时保持它的效率?
你可以替换它:
string.Join(", ", propertyNames.Select(prop => "[" + prop + "]"))
与c# 6字符串插值:
string.Join(", ", propertyNames.Select(prop => $"[{prop}]"))
在这两种情况下,区别只是语义上的,这并不重要。字符串连接,就像在选择中那样,不是问题。编译器仍然只为它创建1个新字符串(而不是4个,每个段一个,第四个为联合字符串)。
把它们放在一起:
var result = string.Join(", ", enumerable.Select(v => $"[{v.TheInterestingStuff}]"));
因为foreach
的主体是更复杂的,适合字符串插值范围,你可以只是删除字符串的最后N个字符一旦计算,如KooKiz建议。
string separator = ", ";
foreach(var part in interestingParts)
{
stringBuilder.Append("[");
stringBuilder.Append(part);
stringBuilder.Append("]");
if (someCondition(part))
{
// Append more stuff
}
else
{
// Append other thingd
}
stringBuilder.Append(separator);
}
stringBuilder.Length = stringBuilder.Lenth - separator;
在任何情况下,我认为为了更好地封装,循环范围的内容应该放在一个单独的函数中,该函数将接收part
和separator
,并将返回输出字符串。它也可以是user734028
StringBuilder
的扩展方法。使用StringBuilder
的Aggregate
扩展方法
如果您的集合很大,将比连接字符串更有效
var builder = new StringBuilder();
list.Aggregate(builder, (sb, person) =>
{
sb.Append(",");
sb.Append("[");
sb.Append(person.Name);
sb.Append("]");
return sb;
});
builder.Remove(0, 1); // Remove first comma
由于纯foreach
总是比LINQ
更有效,所以只需更改分隔逗号的逻辑
var builder = new StringBuilder();
foreach(var part in enumerable.Select(v => v.TheInterestingStuff))
{
builder.Append(", ");
builder.Append("[");
builder.Append(part);
builder.Append("]");
}
builder.Remove(0, 2); //Remove first comma and space
总解决方案:
var answer = interestingParts.Select(v => "[" + v + "]").Aggregate((a, b) => a + ", " + b);
序列化解决方案:
var temp = JsonConvert.SerializeObject(interestingParts.Select(x => new[] { x }));
var answer = temp.Substring(1, temp.Length - 2).Replace(",", ", ");
public string BuildString()
{
var enumerable = GetEnumerableFromSomewhere();
var interestingParts = enumerable.Select(v => v.TheInterestingStuff).ToArray();
stringBuilder.Append("This is it: ");
foreach(var part in interestingParts)
{
stringBuilder.AppendPart(part)
}
if (stringBuilder.Length>0)
stringBuilder.Length--;
}
private static void AppendPart(this StringBuilder stringBuilder, InterestingPart part)
{
if (someCondition(part))
{
stringBuilder.Append(string.Format("[{0}] = @{0}", part.Something));
}
else
{
stringBuilder.Append(string.Format("[{0}]", part.Something));
stringBuilder.AppendInFilter(part); //
}
}
现在好多了。
现在讨论一下如何使它非常快。我们可以用parallel, for。但是您会认为(如果您认为)Appends都发生在单个可共享资源上,即StringBuilder,然后您必须将其锁定为Append,这不是那么有效!好吧,如果我们可以说外部函数中for循环的每次迭代都创建一个字符串工件,那么我们就可以有一个字符串数组,在并行for开始之前分配给感兴趣的部分的计数,并且并行for的每个索引将其字符串存储到其各自的索引中。
类似:
string[] iteration_buckets = new string[interestingParts.Length];
System.Threading.Tasks.Parallel.For(0, interestingParts.Length,
(index) =>
{
iteration_buckets[index] = AppendPart(interestingParts[index]);
});
你的函数AppendPart必须被调整,使它成为一个非扩展,只接受一个字符串并返回一个字符串。循环结束后,您可以执行一个字符串。Join来获取一个字符串,这也是你可以用stringBuilder.ToString()来做的。