我有一个简单的linq查询:
var k = people.Select(x=>new{x.ID, x.Name});
然后我想要一个函数或linq lambda,或者用逗号和“ands”输出句子格式的名字。
{1, John}
{2, Mark}
{3, George}
到
"1:John, 2:Mark and 3:George"
我很好用硬编码ID + ":" + Name
部分,但它可能是一个ToString(),具体取决于linq查询结果的类型。我只是想知道是否有一个简洁的方法来使用linq或String.Format()。
答案 0 :(得分:6)
为什么选择Linq?
StringBuilder sb = new StringBuilder();
for(int i=0;i<k.Count();i++)
{
sb.Append(String.Format("{0}:{1}", k[i].ID, k[i].Name);
if(i + 2 < k.Count())
sb.Append(", ");
else if(i + 1 < k.Count())
sb.Append(" and ");
}
真的,Linq让你做的就是隐藏循环。
另外,请确保您想要或不想要“Oxford Comma”;这个算法不会插入一个,但是会有一个小的改动(在除了最后一个之外的每个元素之后附加逗号和空格,并且在倒数第二个之后追加“和”)。
答案 1 :(得分:6)
public string ToPrettyCommas<T>(
List<T> source,
Func<T, string> stringSelector
)
{
int count = source.Count;
Func<int, string> prefixSelector = x =>
x == 0 ? "" :
x == count - 1 ? " and " :
", ";
StringBuilder sb = new StringBuilder();
for(int i = 0; i < count; i++)
{
sb.Append(prefixSelector(i));
sb.Append(stringSelector(source[i]));
}
string result = sb.ToString();
return result;
}
跟:
string result = ToPrettyCommas(people, p => p.ID.ToString() + ":" + p.Name);
答案 2 :(得分:3)
只是为了好玩,这里真正使用功能性LINQ - 没有循环而没有StringBuilder
。当然,这很慢。
var list = new[] { new { ID = 1, Name = "John" },
new { ID = 2, Name = "Mark" },
new { ID = 3, Name = "George" } };
var resultAggr = list
.Select(item => item.ID + ":" + item.Name)
.Aggregate(new { Sofar = "", Next = (string) null },
(agg, next) => new { Sofar = agg.Next == null ? "" :
agg.Sofar == "" ? agg.Next :
agg.Sofar + ", " + agg.Next,
Next = next });
var result = resultAggr.Sofar == "" ? resultAggr.Next :
resultAggr.Sofar + " and " + resultAggr.Next;
// Prints 1:John, 2:Mark and 3:George
Console.WriteLine(result);
答案 3 :(得分:1)
与其他人一样,这并不比使用字符串构建器更好,但你可以去(忽略ID,你可以添加它):
IEnumerable<string> names = new[] { "Tom", "Dick", "Harry", "Abe", "Bill" };
int count = names.Count();
string s = String.Join(", ", names.Take(count - 2)
.Concat(new [] {String.Join(" and ", names.Skip(count - 2))}));
这种做法几乎滥用Skip
和Take
取消负数的能力,String.Join
愿意接受一个参数,因此适用于一,二或更多字符串。
答案 4 :(得分:1)
使用为您提供索引的Select操作,可以将其写为ONE LINE扩展方法:
public static string ToAndList<T>(this IEnumerable<T> list, Func<T, string> formatter)
{
return string.Join(" ", list.Select((x, i) => formatter(x) + (i < list.Count() - 2 ? ", " : (i < list.Count() - 1 ? " and" : ""))));
}
e.g。
var list = new[] { new { ID = 1, Name = "John" },
new { ID = 2, Name = "Mark" },
new { ID = 3, Name = "George" } }.ToList();
Console.WriteLine(list.ToAndList(x => (x.ID + ": " + x.Name)));
答案 5 :(得分:0)
这可以是您实现目标的方式
var list = new[] { new { ID = 1, Name = "John" },
new { ID = 2, Name = "Mark" },
new { ID = 3, Name = "George" }
}.ToList();
int i = 0;
string str = string.Empty;
var k = list.Select(x => x.ID.ToString() + ":" + x.Name + ", ").ToList();
k.ForEach(a => { if (i < k.Count() - 1) { str = str + a; } else { str = str.Substring(0, str.Length -2) + " and " + a.Replace("," , ""); } i++; });
答案 6 :(得分:0)
public static string ToListingCommaFormat(this List<string> stringList)
{
switch(stringList.Count)
{
case 0:
return "";
case 1:
return stringList[0];
case 2:
return stringList[0] + " and " + stringList[1];
default:
return String.Join(", ", stringList.GetRange(0, stringList.Count-1))
+ ", and " + stringList[stringList.Count - 1];
}
}
这种方法比“高效”方法更快。 Gabe发布的加入方法。对于一个和两个项目,它的速度要快很多倍,对于5-6个字符串,速度要快10%。 LINQ没有任何依赖性。对于小型数组,String.Join比StringBuilder更快,这对于人类可读的文本来说是典型的。在语法中,这些被称为listing commas,并且应始终包括最后一个逗号以避免歧义。以下是生成的代码:
people.Select(x=> x.ID.ToString() + ":" + x.Name).ToList().ToListingCommaFormat();
答案 7 :(得分:0)
static public void Linq1()
{
var k = new[] { new[] { "1", "John" }, new[] { "2", "Mark" }, new[] { "3", "George" } };
Func<string[], string> showPerson = p => p[0] + ": " + p[1];
var res = k.Skip(1).Aggregate(new StringBuilder(showPerson(k.First())),
(acc, next) => acc.Append(next == k.Last() ? " and " : ", ").Append(showPerson(next)));
Console.WriteLine(res);
}
可以通过将k.Last()计算移动到循环
之前进行优化答案 8 :(得分:0)
这是一个使用我的answer到Eric Lippert's Challenge稍微修改过的版本,这是IMHO最简洁易懂的逻辑(如果您熟悉LINQ)。
static string CommaQuibblingMod<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); //removed braces
}
//usage
var items = k.Select(item => String.Format("{0}:{1}", item.ID, item.Name));
string formatted = CommaQuibblingMod(items);
答案 9 :(得分:0)
这是一种不使用LINQ的方法,但可能效率最高:
public static string Join<T>(this IEnumerable<T> list,
string joiner,
string lastJoiner = null)
{
StringBuilder sb = new StringBuilder();
string sep = null, lastItem = null;
foreach (T item in list)
{
if (lastItem != null)
{
sb.Append(sep);
sb.Append(lastItem);
sep = joiner;
}
lastItem = item.ToString();
}
if (lastItem != null)
{
if (sep != null)
sb.Append(lastJoiner ?? joiner);
sb.Append(lastItem);
}
return sb.ToString();
}
Console.WriteLine(people.Select(x => x.ID + ":" + x.Name).Join(", ", " and "));
因为它永远不会创建一个列表,所以查看一个元素两次,或者向StringBuilder添加额外的东西,我认为你不能提高效率。它也适用于列表中的0,1和2个元素(以及更多,显然)。
答案 10 :(得分:0)
我已经完善了我以前的答案,我相信这是最优雅的解决方案 但是它只适用于不在集合中重复的引用类型(否则我们必须使用不同的方法来查找项目是第一个还是最后一个)。
享受!
var firstGuy = guys.First();
var lastGuy = guys.Last();
var getSeparator = (Func<Guy, string>)
(guy => {
if (guy == firstGuy) return "";
if (guy == lastGuy) return " and ";
return ", ";
});
var formatGuy = (Func<Guy, string>)
(g => string.Format("{0}:{1}", g.Id, g.Name));
// 1:John, 2:Mark and 3:George
var summary = guys.Aggregate("",
(sum, guy) => sum + getSeparator(guy) + formatGuy(guy));
答案 11 :(得分:0)
这个怎么样?
var k = people.Select(x=>new{x.ID, x.Name});
var stringified = people
.Select(x => string.Format("{0} : {1}", x.ID, x.Name))
.ToList();
return string.Join(", ", stringified.Take(stringified.Count-1).ToArray())
+ " and " + stringified.Last();
答案 12 :(得分:0)
StringBuilder方法
这是Aggregate
StringBuilder
。有一些位置确定用于清理字符串并插入“和”,但它都是在StringBuilder
级别完成的。
var people = new[]
{
new { Id = 1, Name = "John" },
new { Id = 2, Name = "Mark" },
new { Id = 3, Name = "George" }
};
var sb = people.Aggregate(new StringBuilder(),
(s, p) => s.AppendFormat("{0}:{1}, ", p.Id, p.Name));
sb.Remove(sb.Length - 2, 2); // remove the trailing comma and space
var last = people.Last();
// index to last comma (-2 accounts for ":" and space prior to last name)
int indexComma = sb.Length - last.Id.ToString().Length - last.Name.Length - 2;
sb.Remove(indexComma - 1, 1); // remove last comma between last 2 names
sb.Insert(indexComma, "and ");
// 1:John, 2:Mark and 3:George
Console.WriteLine(sb.ToString());
可以使用String.Join
方法,但“and”插入和逗号删除会生成~2个新字符串。
正则表达式方法
这是使用正则表达式的另一种方法,这是非常容易理解的(没什么太神秘的)。
var people = new[]
{
new { Id = 1, Name = "John" },
new { Id = 2, Name = "Mark" },
new { Id = 3, Name = "George" }
};
var joined = String.Join(", ", people.Select(p => p.Id + ":" + p.Name).ToArray());
Regex rx = new Regex(", ", RegexOptions.RightToLeft);
string result = rx.Replace(joined, " and ", 1); // make 1 replacement only
Console.WriteLine(result);
模式只是", "
。神奇之处在于RegexOptions.RightToLeft
,它使匹配从右边发生,从而使替换发生在最后一个逗号。没有静态Regex
方法接受RegexOptions
的替换次数,因此实例用法。
答案 13 :(得分:0)
你们都太复杂了:
var list = k.Select(x => x.ID + ":" + x.Name).ToList();
var str = list.LastOrDefault();
str = (list.Count >= 2 ? list[list.Count - 2] + " and " : null) + str;
str = string.Join(", ", list.Take(list.Count - 2).Concat(new[]{str}));
答案 14 :(得分:0)
这不是很好,但会使用LINQ
完成工作string s = string.Join(",", k.TakeWhile(X => X != k.Last()).Select(X => X.Id + ":" + X.Name).ToArray()).TrimEnd(",".ToCharArray()) + " And " + k.Last().Id + ":" + k.Last().Name;
答案 15 :(得分:0)
改善(希望)KeithS的回答:
string nextBit = "";
var sb = new StringBuilder();
foreach(Person person in list)
{
sb.Append(nextBit);
sb.Append(", ");
nextBit = String.Format("{0}:{1}", person.ID, person.Name);
}
sb.Remove(sb.Length - 3, 2);
sb.Append(" and ");
sb.Append(nextBit);
答案 16 :(得分:-1)
有很多方法可以优化它,因为它效率不高,但是这样的方法可能有效:
var k = people.Select(x => new {x.ID, x.Name}).ToList();
var last = k.Last();
k.Aggregate(new StringBuilder(), (sentence, item) => {
if (sentence.Length > 0)
{
if (item == last)
sentence.Append(" and ");
else
sentence.Append(", ");
}
sentence.Append(item.ID).Append(":").Append(item.Name);
return sentence;
});