想象一下这些输入和结果:
[] -> ""
["Hello World!"] -> "Hello World!"
["Apples", "bananas"] -> "Apples, and bananas" (put your grammar books away)
["Lions", "Tigers", "Bears"] -> "Lions, Tigers, and Bears" (oh my!)
现在,假设输入都是IEnumerable<string>
。有什么好处(哪里好可能包含“小而整齐”,“易于理解”,“使用LINQ的全部功能”,或其他只要有理由)在C#中编写一个函数来执行此操作?我真的想避免“强制循环”方法。
我目前的方法如下:
string Commaize (IEnumerable<string> list) {
if (list.Count() > 1) {
list = list.Take(list.Count() - 2).Concat(
new[] { list.Reverse().Take(2).Reverse()
.Aggregate((a, b) => a + " and " + b) });
}
return String.Join(", ", list.ToArray());
}
但它感觉不是很“好”。它适用于.NET3.5,因此这里需要ToArray()
位。如果list
为空,则结果为UB。
答案 0 :(得分:7)
与其他答案(CodeInChaos发布的答案除外)不同,此实现仅枚举输入序列一次。如果枚举它的成本很高(例如数据库查询,Web服务调用......)
,这可能很重要string Commaize (IEnumerable<string> list)
{
string previous = null;
StringBuilder sb = new StringBuilder();
foreach(string s in list)
{
if (previous != null)
sb.AppendFormat("{0}, ", previous);
previous = s;
}
if (previous != null)
{
if (sb.Length > 0)
sb.AppendFormat("and {0}", previous);
else
sb.Append(previous);
}
return sb.ToString();
}
答案 1 :(得分:1)
string Commaize (IEnumerable<string> sequence)
{
IList<string> list=sequence as IList<string>;
if(list==null)
list=sequence.ToList();
if(list.Count==0)
return "";
else if(list.Count==1)
return list.First();
else
return String.Join(", ", list.Take(list.Count-1).ToArray()) + " and " + list.Last();
}
这样的开销是分配一些额外的数组(一个ToList()和一个ToArray()调用,它们可能都使用指数增长数组的分配,因此分配的数组的数量大于2)。 / p>
答案 2 :(得分:1)
从链接的问题,扩展方法:
public static string ToAndList<T>(this IEnumerable<T> list)
{
return string.Join(" ", list.Select((x, i) => x.ToString() + (i < list.Count() - 2 ? ", " : (i < list.Count() - 1 ? " and" : ""))));
}
编辑注意:我认为我们可以放心地假设英语句子不会在重复使用Count()和/或未来的编译器可能会导致任何性能问题为我们优化这一点。但是,是的,如果你想为可以有效实现Count的可枚举集合优化它,你可以将Count()移出语句。
答案 3 :(得分:1)
string Commaize (IEnumerable<string> list) {
var last = list.LastOrDefault();
return (last != null) ?
list.Aggregate((acc,x) => acc + ", " + (x == last ? "and " : "") + x) :
string.Empty;
}
对于10,000个字符串的列表,它在0.5秒内运行(与在大约.005秒内运行的Thomas'相比)。不是最快的,但我确实喜欢可读性。
修改强> 的
string Commaize (IEnumerable<string> list) {
var enumer = list.GetEnumerator();
if (enumer.MoveNext()) {
var c = enumer.Current;
return (enumer.MoveNext()) ?
list.Aggregate((acc,x) => acc + ", " + (!enumer.MoveNext() ? "and " : "") + x) :
c;
}
return string.Empty;
}
这个版本没有第一个版本的等式问题,但是以可读性为代价,这实际上是第一个函数唯一的用途。
答案 4 :(得分:0)
这是我在implementation中找到的Eric Lippert's challenge(使用.NET 4.0):
static string CommaQuibbling<T>(IEnumerable<T> items)
{
int count = items.Count();
var quibbled = items.Select((Item, index) => new { Item, Group = (count - index - 2) > 0})
.GroupBy(item => item.Group, item => item.Item)
.Select(g => g.Key
? String.Join(", ", g)
: String.Join(" and ", g));
return "{" + String.Join(", ", quibbled) + "}";
}
如果需要,请添加牛津逗号,如果不需要,请删除额外的括号。