代码之家  ›  专栏  ›  技术社区  ›  leora Matt Lacey

将对象数组转换为连接字符串

  •  11
  • leora Matt Lacey  · 技术社区  · 15 年前

    如果我有:

     List<Car>
    

    汽车在哪里:

     public class Car
     {
          public int Year;
          public string Name;
     }
    

    我想使用这个数组,通过“,”创建一个连接字符串。

    所以它会返回:

    "Toyota, Ford, Chevy"
    

    我可以这样手动操作:

      private static string CreateConcatenatedList(List<Car> parts_)
        {
            StringBuilder b = new StringBuilder();
            foreach (Car bp in parts_)
            {
                b.Append(bp.Name + ", ");
            }
            b.Remove(b.Length - 2, 2);
            return b.ToString();
        }
    

    但我想可能有更优雅的方式

    6 回复  |  直到 15 年前
        1
  •  10
  •   Lee    15 年前
    List<Car> cars = //whatever;
    string concat = String.Join(",", cars.Select(c => c.Name).ToArray());
    

    编辑:如果担心创建中间数组,也可以使用聚合:

    string concat = cars.Select(c => c.Name).Aggregate(new StringBuilder(), (sb, current) =>
    {
        return sb.Length == 0 ? sb.Append(current) : sb.AppendFormat(",{0}", current);
    }).ToString();
    
        2
  •  6
  •   Philip Daubmeier    15 年前

    因为你在对lees的评论中问到答案是更快/更慢还是更少的代码。我试了一下,写了一个小型汽车班:

    public class Car
    {
        public string Name { get; set; }
        public Car(string name) { Name = name; }
    }
    

    使用随机生成的长度为5-10的字符串进行测试:

    private static Random random = new Random((int)DateTime.Now.Ticks);
    private static string RandomString(int min, int max)
    {
        string str = "";
        int size = random.Next(min, max + 1);
        for (int i = 0; i < size; i++)
            str += Convert.ToChar(Convert.ToInt32(
                           Math.Floor(26 * random.NextDouble() + 65)));
        return str;
    }
    
    public static void MeassureTicks(int numberCars, int minLength, int maxLength)
    {
        // Generate random list
        List<Car> cars = Enumerable.Range(0, numberCars)
                         .Select(x => new Car(RandomString(
                                 minLength, maxLength))).ToList();
    
        Stopwatch sw1 = new Stopwatch(), sw2 = new Stopwatch(),
                  sw3 = new Stopwatch(), sw4 = new Stopwatch();
    
        sw1.Start();
        string concat1 = CreateConcatenatedList(cars);
        sw1.Stop();
        sw2.Start();
        string concat2 = String.Join(",", cars.Select(c => c.Name).ToArray());
        sw2.Stop();
        sw3.Start();
        if (numberCars <= 5000)
        {
            string concat3 = cars.Select(c => c.Name).Aggregate("",
                    (str, current) =>
                    {
                        return str.Length == 0 ? str = current :
                               str += "," + current;
                    }).ToString();
        }
        sw3.Stop();
        sw4.Start();
        string concat4 = cars.Select(c => c.Name).Aggregate(
                new StringBuilder(), (sb, current) =>
                {
                    return sb.Length == 0 ? sb.Append(current) :
                           sb.AppendFormat(",{0}", current);
                }).ToString();
        sw4.Stop();
    
        Console.WriteLine(string.Format("{0} car strings joined:\n" +
                    "\tYour method:                  {1} ticks\n" + 
                    "\tLinq+String.Join:             {2} ticks\n" + 
                    "\tLinq+Aggregate+String.Concat: {3} ticks\n" + 
                    "\tLinq+Aggregate+StringBuilder: {4} ticks\n",
                    cars.Count, sw1.ElapsedTicks, sw2.ElapsedTicks, 
                    numberCars <= 5000 ? sw3.ElapsedTicks.ToString() : "-", 
                    sw4.ElapsedTicks));
    

    更新: 我现在也在尝试使用聚合的两种方法。

    我的电脑上有一些不同数量的汽车的输出:

    5 car strings joined:
            Your method:                  14 ticks
            Linq+String.Join:             20 ticks
            Linq+Aggregate+String.Concat: 11 ticks
            Linq+Aggregate+StringBuilder: 15 ticks
    
    50 car strings joined:
            Your method:                  50 ticks
            Linq+String.Join:             45 ticks
            Linq+Aggregate+String.Concat: 70 ticks
            Linq+Aggregate+StringBuilder: 73 ticks
    
    500 car strings joined:
            Your method:                  355 ticks
            Linq+String.Join:             348 ticks
            Linq+Aggregate+String.Concat: 5365 ticks
            Linq+Aggregate+StringBuilder: 619 ticks
    
    5000 car strings joined:
            Your method:                  3584 ticks
            Linq+String.Join:             3357 ticks
            Linq+Aggregate+String.Concat: 379635 ticks
            Linq+Aggregate+StringBuilder: 6078 ticks
    
    50000 car strings joined:
            Your method:                  33705 ticks
            Linq+String.Join:             34082 ticks
            Linq+Aggregate+String.Concat: - ticks
            Linq+Aggregate+StringBuilder: 92839 ticks
    
    500000 car strings joined:
            Your method:                  508439 ticks
            Linq+String.Join:             376339 ticks
            Linq+Aggregate+String.Concat: - ticks
            Linq+Aggregate+StringBuilder: 616048 ticks
    

    LINQ+ String.Join 方法确实有点快 更少的代码。与 StringBuilter 缩放得很好(不像字符串串联),但速度有点慢。所以要么用你的方法,要么用LINQ+ 连接字符串 这是一个很好的一行程序,也很容易阅读。

        3
  •  5
  •   Roger Johansson    15 年前
    List<Car> cars = ....
    var result = string.Join(",", cars.Select(car => car.Name).ToArray());
    
        4
  •  1
  •   Klaus Byskov Pedersen    15 年前

    我认为你真正想要的是:

    "Toyota, Ford, Chevy" 
    

    而不是:

    "Toyota", "Ford", "Chevy" 
    

    如你问题中所写。可以这样实现:

    var cars = new List<Car>();
    
    var delimitedString = string.Join(", ", cars.Select(c => c.Name).ToArray());
    
        5
  •  1
  •   gvaish    15 年前
    ArrayList<Car> cars = ...
    string finalValue = string.Join(",", cars.Select(c => c.Name).ToArray());
    
        6
  •  1
  •   juharr    15 年前

    我为这种情况编写了以下扩展方法。它使用了一个字符串生成器和聚合而不是string.join和一个数组来稍微提高性能。

    public static string Concatenate(
        this IEnumerable<string> collection, 
        string separator)
    {
        return collection
            .Skip(1)
            .Aggregate(
                new StringBuilder().Append(collection.First()),
                (b, s) => b.Append(separator).Append(s))
            .ToString();
    }
    

    那么在你的情况下,这只是

    cars.Select(c=>Name).Concatenate(", ");