使用 LINQ 连接字符串

发布于 2024-07-07 17:43:42 字数 271 浏览 5 评论 0 原文

在 LINQ 中编写老式的:

StringBuilder sb = new StringBuilder();
if (strings.Count > 0)
{
    foreach (string s in strings)
    {
        sb.Append(s + ", ");
    }
    sb.Remove(sb.Length - 2, 2);
}
return sb.ToString();

... 的最有效方法是什么?

What is the most efficient way to write the old-school:

StringBuilder sb = new StringBuilder();
if (strings.Count > 0)
{
    foreach (string s in strings)
    {
        sb.Append(s + ", ");
    }
    sb.Remove(sb.Length - 2, 2);
}
return sb.ToString();

...in LINQ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

拍不死你 2024-07-14 17:43:42

此答案显示了问题中要求的 LINQ (Aggregate) 的用法,并不适合日常使用。 因为这不使用 StringBuilder,所以对于很长的序列来说,它的性能会很糟糕。 对于常规代码,请使用 String.Join,如其他答案

使用聚合像这样的查询:

string[] words = { "one", "two", "three" };
var res = words.Aggregate(
   "", // start with empty string to handle empty list case.
   (current, next) => current + ", " + next);
Console.WriteLine(res);

输出:

, one, two, three

聚合是一个函数,它接受值的集合并返回标量值。 T-SQL 的示例包括最小值、最大值和总和。 VB 和 C# 都支持聚合。 VB 和 C# 都支持聚合作为扩展方法。 使用点表示法,只需调用 IEnumerable< 上的方法即可/a> 对象。

请记住,聚合查询会立即执行。

详细信息 - MSDN:聚合查询


如果您确实想使用 Aggregate 使用 StringBuilder 使用变体>CodeMonkeyKing 与常规 String.Join 的代码大致相同,包括对大量对象的良好性能:

 var res = words.Aggregate(
     new StringBuilder(), 
     (current, next) => current.Append(current.Length == 0? "" : ", ").Append(next))
     .ToString();

This answer shows usage of LINQ (Aggregate) as requested in the question and is not intended for everyday use. Because this does not use a StringBuilder it will have horrible performance for very long sequences. For regular code use String.Join as shown in the other answer

Use aggregate queries like this:

string[] words = { "one", "two", "three" };
var res = words.Aggregate(
   "", // start with empty string to handle empty list case.
   (current, next) => current + ", " + next);
Console.WriteLine(res);

This outputs:

, one, two, three

An aggregate is a function that takes a collection of values and returns a scalar value. Examples from T-SQL include min, max, and sum. Both VB and C# have support for aggregates. Both VB and C# support aggregates as extension methods. Using the dot-notation, one simply calls a method on an IEnumerable object.

Remember that aggregate queries are executed immediately.

More information - MSDN: Aggregate Queries


If you really want to use Aggregate use variant using StringBuilder proposed in comment by CodeMonkeyKing which would be about the same code as regular String.Join including good performance for large number of objects:

 var res = words.Aggregate(
     new StringBuilder(), 
     (current, next) => current.Append(current.Length == 0? "" : ", ").Append(next))
     .ToString();
你的往事 2024-07-14 17:43:42
return string.Join(", ", strings.ToArray());

在 .Net 4 中,字符串有一个新的重载。 Join 接受 IEnumerable。 代码将如下所示:

return string.Join(", ", strings);
return string.Join(", ", strings.ToArray());

In .Net 4, there's a new overload for string.Join that accepts IEnumerable<string>. The code would then look like:

return string.Join(", ", strings);
迷你仙 2024-07-14 17:43:42

为什么使用 Linq?

string[] s = {"foo", "bar", "baz"};
Console.WriteLine(String.Join(", ", s));

据我所知,它完美地工作并接受任何 IEnumerable 。 这里不需要聚合任何东西,这要慢得多。

Why use Linq?

string[] s = {"foo", "bar", "baz"};
Console.WriteLine(String.Join(", ", s));

That works perfectly and accepts any IEnumerable<string> as far as I remember. No need Aggregate anything here which is a lot slower.

寄人书 2024-07-14 17:43:42

您看过聚合扩展方法吗?

var sa = (new[] { "yabba", "dabba", "doo" }).Aggregate((a,b) => a + "," + b);

Have you looked at the Aggregate extension method?

var sa = (new[] { "yabba", "dabba", "doo" }).Aggregate((a,b) => a + "," + b);
超可爱的懒熊 2024-07-14 17:43:42

我的代码中的真实示例:

return selected.Select(query => query.Name).Aggregate((a, b) => a + ", " + b);

查询是一个具有 Name 属性的对象,该属性是一个字符串,我想要所选列表上所有查询的名称,以逗号分隔。

Real example from my code:

return selected.Select(query => query.Name).Aggregate((a, b) => a + ", " + b);

A query is an object that has a Name property which is a string, and I want the names of all the queries on the selected list, separated by commas.

御弟哥哥 2024-07-14 17:43:42

这是我在查看其他答案和类似问题中解决的问题后决定采用的组合 Join/Linq 方法(即聚合和连接因 0 个元素而失败)。

string Result = String.Join(",", split.Select(s => s.Name));

或(如果 s 不是字符串)

string Result = String.Join(",", split.Select(s => s.ToString()));

  • 简单
  • 易读和理解
  • 的通用元素
  • 允许使用对象或对象属性
  • 处理0 长度元素的情况
  • 可以与额外的 Linq 过滤一起使用,
  • 表现良好(至少根据我的经验)
  • 不需要(手动)创建额外的对象(例如 StringBuilder)来实现

当然Join 处理了有时会潜入其他方法中的讨厌的最后逗号(forforeach),这就是我首先寻找 Linq 解决方案的原因。

Here is the combined Join/Linq approach I settled on after looking at the other answers and the issues addressed in a similar question (namely that Aggregate and Concatenate fail with 0 elements).

string Result = String.Join(",", split.Select(s => s.Name));

or (if s is not a string)

string Result = String.Join(",", split.Select(s => s.ToString()));

  • Simple
  • easy to read and understand
  • works for generic elements
  • allows using objects or object properties
  • handles the case of 0-length elements
  • could be used with additional Linq filtering
  • performs well (at least in my experience)
  • doesn't require (manual) creation of an additional object (e.g. StringBuilder) to implement

And of course Join takes care of the pesky final comma that sometimes sneaks into other approaches (for, foreach), which is why I was looking for a Linq solution in the first place.

甜尕妞 2024-07-14 17:43:42

您可以在 Aggregate 中使用 StringBuilder:(

  List<string> strings = new List<string>() { "one", "two", "three" };

  StringBuilder sb = strings
    .Select(s => s)
    .Aggregate(new StringBuilder(), (ag, n) => ag.Append(n).Append(", "));

  if (sb.Length > 0) { sb.Remove(sb.Length - 2, 2); }

  Console.WriteLine(sb.ToString());

其中的 Select 只是为了表明您可以执行更多 LINQ 操作。)

You can use StringBuilder in Aggregate:

  List<string> strings = new List<string>() { "one", "two", "three" };

  StringBuilder sb = strings
    .Select(s => s)
    .Aggregate(new StringBuilder(), (ag, n) => ag.Append(n).Append(", "));

  if (sb.Length > 0) { sb.Remove(sb.Length - 2, 2); }

  Console.WriteLine(sb.ToString());

(The Select is in there just to show you can do more LINQ stuff.)

蓝戈者 2024-07-14 17:43:42

StringBuilder 与 Select 和 Select 的快速性能数据 超过 3000 个元素的聚合案例:

单元测试 - 持续时间(秒)
LINQ_StringBuilder - 0.0036644
LINQ_Select.Aggregate - 1.8012535

    [TestMethod()]
    public void LINQ_StringBuilder()
    {
        IList<int> ints = new List<int>();
        for (int i = 0; i < 3000;i++ )
        {
            ints.Add(i);
        }
        StringBuilder idString = new StringBuilder();
        foreach (int id in ints)
        {
            idString.Append(id + ", ");
        }
    }
    [TestMethod()]
    public void LINQ_SELECT()
    {
        IList<int> ints = new List<int>();
        for (int i = 0; i < 3000; i++)
        {
            ints.Add(i);
        }
        string ids = ints.Select(query => query.ToString())
                         .Aggregate((a, b) => a + ", " + b);
    }

quick performance data for the StringBuilder vs Select & Aggregate case over 3000 elements:

Unit test - Duration (seconds)
LINQ_StringBuilder - 0.0036644
LINQ_Select.Aggregate - 1.8012535

    [TestMethod()]
    public void LINQ_StringBuilder()
    {
        IList<int> ints = new List<int>();
        for (int i = 0; i < 3000;i++ )
        {
            ints.Add(i);
        }
        StringBuilder idString = new StringBuilder();
        foreach (int id in ints)
        {
            idString.Append(id + ", ");
        }
    }
    [TestMethod()]
    public void LINQ_SELECT()
    {
        IList<int> ints = new List<int>();
        for (int i = 0; i < 3000; i++)
        {
            ints.Add(i);
        }
        string ids = ints.Select(query => query.ToString())
                         .Aggregate((a, b) => a + ", " + b);
    }
难如初 2024-07-14 17:43:42

我总是使用扩展方法:

public static string JoinAsString<T>(this IEnumerable<T> input, string seperator)
{
    var ar = input.Select(i => i.ToString());
    return string.Join(seperator, ar);
}

I always use the extension method:

public static string JoinAsString<T>(this IEnumerable<T> input, string seperator)
{
    var ar = input.Select(i => i.ToString());
    return string.Join(seperator, ar);
}
少钕鈤記 2024-07-14 17:43:42

通过“超级酷的 LINQ 方式”,您可能会谈论 LINQ 通过使用扩展方法使函数式编程变得更加容易接受的方式。 我的意思是,语法糖允许函数以视觉上线性的方式链接(一个接一个),而不是嵌套(一个在另一个里面)。 例如:

int totalEven = Enumerable.Sum(Enumerable.Where(myInts, i => i % 2 == 0));

可以这样写:

int totalEven = myInts.Where(i => i % 2 == 0).Sum();

你可以看到第二个例子如何更容易阅读。 您还可以看到如何添加更多函数,同时减少缩进问题或表达式末尾出现的 Lispy 右括号。

许多其他答案都指出 String.Join 是可行的方法,因为它是最快或最简单的读取方式。 但是,如果您接受我对“超酷 LINQ 方式”的解释,那么答案是使用 String.Join,但将其包装在 LINQ 样式扩展方法中,这样可以允许您可以以视觉上令人愉悦的方式链接您的功能。 因此,如果您想编写 sa.Concatenate(", ") 您只需创建如下内容:

public static class EnumerableStringExtensions
{
   public static string Concatenate(this IEnumerable<string> strings, string separator)
   {
      return String.Join(separator, strings);
   }
}

这将提供与直接调用一样高性能的代码(至少在算法复杂性方面) )并且在某些情况下可能会使代码更具可读性(取决于上下文),特别是当块中的其他代码使用链式函数样式时。

By 'super-cool LINQ way' you might be talking about the way that LINQ makes functional programming a lot more palatable with the use of extension methods. I mean, the syntactic sugar that allows functions to be chained in a visually linear way (one after the other) instead of nesting (one inside the other). For example:

int totalEven = Enumerable.Sum(Enumerable.Where(myInts, i => i % 2 == 0));

can be written like this:

int totalEven = myInts.Where(i => i % 2 == 0).Sum();

You can see how the second example is easier to read. You can also see how more functions can be added with less of the indentation problems or the Lispy closing parens appearing at the end of the expression.

A lot of the other answers state that the String.Join is the way to go because it is the fastest or simplest to read. But if you take my interpretation of 'super-cool LINQ way' then the answer is to use String.Join but have it wrapped in a LINQ style extension method that will allow you to chain your functions in a visually pleasing way. So if you want to write sa.Concatenate(", ") you just need to create something like this:

public static class EnumerableStringExtensions
{
   public static string Concatenate(this IEnumerable<string> strings, string separator)
   {
      return String.Join(separator, strings);
   }
}

This will provide code that is as performant as the direct call (at least in terms of algorithm complexity) and in some cases may make the code more readable (depending on the context) especially if other code in the block is using the chained function style.

疧_╮線 2024-07-14 17:43:42

这里它使用纯 LINQ 作为单个表达式:

static string StringJoin(string sep, IEnumerable<string> strings) {
  return strings
    .Skip(1)
    .Aggregate(
       new StringBuilder().Append(strings.FirstOrDefault() ?? ""), 
       (sb, x) => sb.Append(sep).Append(x));
}

而且速度非常快!

Here it is using pure LINQ as a single expression:

static string StringJoin(string sep, IEnumerable<string> strings) {
  return strings
    .Skip(1)
    .Aggregate(
       new StringBuilder().Append(strings.FirstOrDefault() ?? ""), 
       (sb, x) => sb.Append(sep).Append(x));
}

And its pretty damn fast!

唱一曲作罢 2024-07-14 17:43:42

我将稍微作弊并给出一个新的答案,它似乎总结了这里所有内容中最好的部分,而不是把它放在评论中。

因此,您可以这样一行:

List<string> strings = new List<string>() { "one", "two", "three" };

string concat = strings        
    .Aggregate(new StringBuilder("\a"), 
                    (current, next) => current.Append(", ").Append(next))
    .ToString()
    .Replace("\a, ",string.Empty); 

编辑:您要么想首先检查空枚举,要么添加.Replace("\a",string.Empty); 到表达式的末尾。 我想我可能是想变得有点太聪明了。

@a.friend 的答案可能性能稍好一些,我不确定替换与删除相比在幕后做了什么。 唯一的其他警告,如果出于某种原因你想连接以 \a 结尾的字符串,你会丢失分隔符......我发现这不太可能。 如果是这种情况,您确实有 其他奇特的角色可供选择。

I'm going to cheat a little and throw out a new answer to this that seems to sum up the best of everything on here instead of sticking it inside of a comment.

So you can one line this:

List<string> strings = new List<string>() { "one", "two", "three" };

string concat = strings        
    .Aggregate(new StringBuilder("\a"), 
                    (current, next) => current.Append(", ").Append(next))
    .ToString()
    .Replace("\a, ",string.Empty); 

Edit: You'll either want to check for an empty enumerable first or add an .Replace("\a",string.Empty); to the end of the expression. Guess I might have been trying to get a little too smart.

The answer from @a.friend might be slightly more performant, I'm not sure what Replace does under the hood compared to Remove. The only other caveat if some reason you wanted to concat strings that ended in \a's you would lose your separators... I find that unlikely. If that is the case you do have other fancy characters to choose from.

满地尘埃落定 2024-07-14 17:43:42

这里有很多选择。 您可以使用 LINQ 和 StringBuilder,这样您也可以获得性能,如下所示:

StringBuilder builder = new StringBuilder();
List<string> MyList = new List<string>() {"one","two","three"};

MyList.ForEach(w => builder.Append(builder.Length > 0 ? ", " + w : w));
return builder.ToString();

Lots of choices here. You can use LINQ and a StringBuilder so you get the performance too like so:

StringBuilder builder = new StringBuilder();
List<string> MyList = new List<string>() {"one","two","three"};

MyList.ForEach(w => builder.Append(builder.Length > 0 ? ", " + w : w));
return builder.ToString();
归途 2024-07-14 17:43:42

您可以非常有效地将 LINQ 和 string.join() 结合起来。 在这里,我从字符串中删除一个项目。 还有更好的方法可以做到这一点,但这里是:

filterset = String.Join(",",
                        filterset.Split(',')
                                 .Where(f => mycomplicatedMatch(f,paramToMatch))
                       );

You can combine LINQ and string.join() quite effectively. Here I am removing an item from a string. There are better ways of doing this too but here it is:

filterset = String.Join(",",
                        filterset.Split(',')
                                 .Where(f => mycomplicatedMatch(f,paramToMatch))
                       );
谎言月老 2024-07-14 17:43:42

在使用 linq 解析 IIS 日志文件时,我做了以下快速而肮脏的操作,它在 100 万行时工作得很好(15 秒),尽管在尝试 200 万行时出现内存不足错误。

    static void Main(string[] args)
    {

        Debug.WriteLine(DateTime.Now.ToString() + " entering main");

        // USED THIS DOS COMMAND TO GET ALL THE DAILY FILES INTO A SINGLE FILE: copy *.log target.log 
        string[] lines = File.ReadAllLines(@"C:\Log File Analysis\12-8 E5.log");

        Debug.WriteLine(lines.Count().ToString());

        string[] a = lines.Where(x => !x.StartsWith("#Software:") &&
                                      !x.StartsWith("#Version:") &&
                                      !x.StartsWith("#Date:") &&
                                      !x.StartsWith("#Fields:") &&
                                      !x.Contains("_vti_") &&
                                      !x.Contains("/c$") &&
                                      !x.Contains("/favicon.ico") &&
                                      !x.Contains("/ - 80")
                                 ).ToArray();

        Debug.WriteLine(a.Count().ToString());

        string[] b = a
                    .Select(l => l.Split(' '))
                    .Select(words => string.Join(",", words))
                    .ToArray()
                    ;

        System.IO.File.WriteAllLines(@"C:\Log File Analysis\12-8 E5.csv", b);

        Debug.WriteLine(DateTime.Now.ToString() + " leaving main");

    }

我使用 linq 的真正原因是为了我之前需要的 Distinct() :

string[] b = a
    .Select(l => l.Split(' '))
    .Where(l => l.Length > 11)
    .Select(words => string.Format("{0},{1}",
        words[6].ToUpper(), // virtual dir / service
        words[10]) // client ip
    ).Distinct().ToArray()
    ;

I did the following quick and dirty when parsing an IIS log file using linq, it worked @ 1 million lines pretty well (15 seconds), although got an out of memory error when trying 2 millions lines.

    static void Main(string[] args)
    {

        Debug.WriteLine(DateTime.Now.ToString() + " entering main");

        // USED THIS DOS COMMAND TO GET ALL THE DAILY FILES INTO A SINGLE FILE: copy *.log target.log 
        string[] lines = File.ReadAllLines(@"C:\Log File Analysis\12-8 E5.log");

        Debug.WriteLine(lines.Count().ToString());

        string[] a = lines.Where(x => !x.StartsWith("#Software:") &&
                                      !x.StartsWith("#Version:") &&
                                      !x.StartsWith("#Date:") &&
                                      !x.StartsWith("#Fields:") &&
                                      !x.Contains("_vti_") &&
                                      !x.Contains("/c$") &&
                                      !x.Contains("/favicon.ico") &&
                                      !x.Contains("/ - 80")
                                 ).ToArray();

        Debug.WriteLine(a.Count().ToString());

        string[] b = a
                    .Select(l => l.Split(' '))
                    .Select(words => string.Join(",", words))
                    .ToArray()
                    ;

        System.IO.File.WriteAllLines(@"C:\Log File Analysis\12-8 E5.csv", b);

        Debug.WriteLine(DateTime.Now.ToString() + " leaving main");

    }

The real reason I used linq was for a Distinct() I neede previously:

string[] b = a
    .Select(l => l.Split(' '))
    .Where(l => l.Length > 11)
    .Select(words => string.Format("{0},{1}",
        words[6].ToUpper(), // virtual dir / service
        words[10]) // client ip
    ).Distinct().ToArray()
    ;
浅忆 2024-07-14 17:43:42

我不久前在博客上谈到了这一点,我所做的正是您正在寻找的:

http://ondevelopment.blogspot.com/2009/02/string-concatenation-made-easy.html

在博客文章中描述如何实现适用于 IEnumerable 和被命名为 Concatenate,这将允许您编写如下内容:

var sequence = new string[] { "foo", "bar" };
string result = sequence.Concatenate();

或更复杂的内容,例如:

var methodNames = typeof(IFoo).GetMethods().Select(x => x.Name);
string result = methodNames.Concatenate(", ");

I blogged about this a while ago, what I did seams to be exactly what you're looking for:

http://ondevelopment.blogspot.com/2009/02/string-concatenation-made-easy.html

In the blog post describe how to implement extension methods that works on IEnumerable and are named Concatenate, this will let you write things like:

var sequence = new string[] { "foo", "bar" };
string result = sequence.Concatenate();

Or more elaborate things like:

var methodNames = typeof(IFoo).GetMethods().Select(x => x.Name);
string result = methodNames.Concatenate(", ");
你的他你的她 2024-07-14 17:43:42

FWIW 我使用 BDN 对 15 个字符串的字符串数组进行了 string.Join.Aggregate 的基准测试:

Method Mean Error StdDev Gen0 Alulated
String_Join 92.99 ns 9.905 ns 0.543 ns 0.0560 352 B
LING_Aggregate 406.00 ns 74.662 ns 4.092 ns 0.4640 2912 B

阵列越大,差距越大

FWIW I benchmarked string.Join vs .Aggregate on a string array of 15 strings using BDN:

Method Mean Error StdDev Gen0 Allocated
String_Join 92.99 ns 9.905 ns 0.543 ns 0.0560 352 B
LING_Aggregate 406.00 ns 74.662 ns 4.092 ns 0.4640 2912 B

The gap increases with bigger arrays

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文