C# LINQ列表为句子格式(插入逗号“和”)
我有一个linq查询,可以执行以下简单操作:C# LINQ列表为句子格式(插入逗号“和”),c#,linq,string,C#,Linq,String,我有一个linq查询,可以执行以下简单操作: var k = people.Select(x=>new{x.ID, x.Name}); 然后,我想要一个函数或linq lambda,或者一些使用逗号和“ands”以句子格式输出名称的东西 到 我可以对ID+:“+Name部分进行硬编码,但根据linq查询结果的类型,它可能是ToString()。我只是想知道是否有一种简洁的方法可以使用linq或String.Format()实现这一点 为什么是林克 StringBuilder sb = n
var k = people.Select(x=>new{x.ID, x.Name});
然后,我想要一个函数或linq lambda,或者一些使用逗号和“ands”以句子格式输出名称的东西
到
我可以对ID+:“+Name
部分进行硬编码,但根据linq查询结果的类型,它可能是ToString()。我只是想知道是否有一种简洁的方法可以使用linq或String.Format()实现这一点 为什么是林克
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 ");
}
StringBuilder sb=新建StringBuilder();
对于(int i=0;i有一些方法可以优化它,因为它不是很有效,但类似的方法可能会奏效:
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;
});
改进(希望如此)基思的答案:
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);
只是为了好玩,这里有一些真正使用函数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);
这并不漂亮,但可以使用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;
你们把事情弄得太复杂了:
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}));
StringBuilder方法
这是一个带有StringBuilder
的聚合
。有一些位置确定是为了清理字符串并插入“and”,但都是在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个新字符串
正则表达式方法
这里有另一种使用regex的方法,这是很容易理解的(没有什么太神秘)
该模式只是“,”
。神奇之处在于RegexOptions.RightToLeft
,它使匹配从右边发生,从而使替换发生在最后一个逗号出现时。没有静态的Regex
方法接受带有RegexOptions
的替换次数,因此实例的用法与rest,这并不比使用字符串生成器好,但您可以这样做(忽略ID,您可以将其添加到中):
IEnumerable names=new[]{“汤姆”、“迪克”、“哈利”、“亚伯”、“比尔”};
int count=names.count();
string s=string.Join(“,”,names.Take(计数-2)
.Concat(新[]{String.Join(“and”,names.Skip(count-2))});
这种方法几乎滥用了跳过和接受接受负数和字符串的能力。加入接受单个参数的意愿,因此它适用于一个、两个或多个字符串。这是实现目标的方法
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++; });
var list=new[]{new{ID=1,Name=“John”},
新的{ID=2,Name=“Mark”},
新的{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
这个怎么样
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();
使用为您提供索引的Select操作,可以将其编写为一行扩展方法:
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" : ""))));
}
我已经完善了我之前的答案,我相信这是迄今为止最优雅的解决方案。
然而,它只适用于集合中不重复的引用类型(否则我们必须使用不同的方法来确定项是否为first/last)
享受吧
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));
var firstGuy=guys.First();
var lastGuy=guys.Last();
var getSeparator=(Func)
(盖伊=>{
如果(guy==firstGuy)返回“”;
如果(guy==lastGuy)返回“and”;
返回“,”;
});
var formatGuy=(Func)
(g=>string.Format(“{0}:{1}”,g.Id,g.Name));
//1:约翰,2:马克,3:乔治
var summary=guys.Aggregate(“,
(sum,guy)=>sum+getSeparator(guy)+formatGuy(guy));
这里有一种方法不使用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 "));
公共静态字符串联接(此IEnumerable列表,
细木工,
字符串lastJoiner=null)
{
StringBuilder sb=新的StringBuilder();
字符串sep=null,lastItem=null;
foreach(列表中的T项)
{
if(lastItem!=null)
{
某人(九月);
(最后一项);
sep=细木工;
}
lastItem=item.ToString();
}
if(lastItem!=null)
{
如果(sep!=null)
某人追加(最后加入者??加入者);
(最后一项);
}
使某人返回字符串();
}
Console.WriteLine(people.Select(x=>x.ID+):“+x.Name.Join(“,”,“and”);
由于它从不创建列表,不查看元素两次,也不向StringBuilder添加额外的内容,因此我认为您无法获得更高的效率。它也适用于列表中的0、1和2个元素(当然还有更多元素)。这里使用的是一个稍微修改过的my版本,它最简洁,逻辑简单(如果您熟悉LINQ)
静态字符串CommaQuibblingMod(IEnumerable 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)
.选择(g=>g.键
?String.Join(“,”g)
:String.Join(“and”,g));
返回字符串。Join(“,”,quibbled);//删除大括号
}
//用法
var items=k.Select(item=>String.Format(“{0}:{1}”,item.ID,item.Name));
字符串格式=CommaQuibblingMod(项目);
静态公共void Linq1()
{
var k=new[]{new[]{“1”,“John”},new[]{“2”,“Mark”},new[]{“3”,“George”};
Func showPerson=p=>p[0]+“:”+p[1];
var res=k.Skip(1).Aggregate(新的StringBuilder(showPerson)(k.First()),
(acc,next)=>acc.Append(next==k.Last()?“and”:“,”).Append(showPerson(next));
欺骗
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();
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);
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" : ""))));
}
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)));
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));
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 "));
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);
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);
}
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];
}
}