Dizeleri birleştirmek için LINQ kullanma


346

Eski okulu yazmanın en etkili yolu nedir:

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();

... LINQ'da mı?


1
Bir şeyler yapmanın başka süper havalı LINQ yollarını keşfettiniz mi?
Robert

3
Seçilen cevap ve diğer tüm seçenekler Linq to Entities'de çalışmaz.
Binoj Antony

3
@Binoj Antony, veritabanınızı dize birleştirme yapmayın.
Amy B

6
@ Pr0fess0rX: Çünkü yapamıyor ve yapmamalı. Diğer veritabanları hakkında bilmiyorum ama SQL Server'da sadece (n) varchar (max) ile sınırlayan varcahr concat (n) var. İş mantığının veri katmanına uygulanmaması gerektiği için olmamalıdır.
the_drow

tam kaynak kodu ve yüksek performans ile herhangi bir son çözüm?
Kiquenet

Yanıtlar:


529

Bu cevap, Aggregatesoruda talep edildiği gibi LINQ ( ) kullanımını gösterir ve günlük kullanım için tasarlanmamıştır. Bu bir kullanmadığı StringBuilderiçin çok uzun diziler için korkunç bir performansa sahip olacaktır. String.JoinDiğer cevapta gösterildiği gibi düzenli kod kullanımı için

Bunun gibi toplu sorguları kullanın:

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);

Bu çıktılar:

, bir, iki, üç

Toplama, bir değer koleksiyonu alan ve skaler bir değer döndüren bir işlevdir. T-SQL örnekleri arasında min, maks ve sum sayılabilir. Hem VB hem de C #, kümeler için desteğe sahiptir. Hem VB hem de C #, uzantı yöntemleri olarak toplamaları destekler. Nokta gösterimini kullanarak, bir kişi IEnumerable nesnesinde bir yöntemi çağırır .

Toplam sorguların hemen yürütüldüğünü unutmayın.

Daha fazla bilgi - MSDN: Toplu Sorgular


Gerçekten CodeMonkeyKing tarafından yorumda önerilen Aggregatekullanarak , çok sayıda nesne için iyi performans da dahil olmak üzere normal kodla aynı olan bir varyantı kullanmak istiyorsanız :StringBuilderString.Join

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

4
İlk örnek "bir, iki, üç" çıktısını vermez, bir, iki, üç "çıktısı verir (Baştaki virgüllere dikkat edin).
Mort

İlk örneğinizde, tohum eklediğiniz ""için kullanılan ilk değer currentboş bir dizedir. Yani, 1 veya daha fazla eleman için, her zaman , dizenin başlangıcında olursunuz .
Michael Yanni

@Mort Bunu düzelttim
sergtk

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

.Net 4, yeni bir var aşırı yük için string.Joinolduğunu kabul IEnumerable<string>. Kod daha sonra şöyle görünecektir:

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

2
Tamam, çözüm Linq kullanmıyor, ama benim için oldukça iyi çalışıyor gibi görünüyor
Mat Roberts

33
ToArray is linq :)
Amy B

18
Bu en doğru cevap. Hem sorudan hem de kabul edilen yanıttan daha hızlıdır ve her kullanıldığında paragraf uzunluğunda bir açıklama gerektiren Aggregate'ten çok daha açıktır.
PRMan


125

Neden Linq kullanıyorsunuz?

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

Mükemmel çalışıyor ve IEnumerable<string>hatırladığım kadarıyla kabul ediyor . AggregateBurada çok daha yavaş olan hiçbir şeye gerek yok.


19
LINQ öğrenmek havalı olabilir ve LINQ, sonu başarmak için sevimli bir araç olabilir, ancak sonuçta gerçek sonucu elde etmek için LINQ kullanmak, en azından söylemek gerekirse, aptalca değilse, kötü olur
Jason Bunting

9
.NET 4.0, kullanımı çok daha kolaylaştıracak bir IEnumerable <string> ve IEnumrable <T> aşırı yüklenmesine sahiptir
Cine

3
Cine'nin işaret ettiği gibi, .NET 4.0 aşırı yüklenmeye sahiptir. Önceki sürümler yoktur. Yine String.Join(",", s.ToArray())de eski sürümlerde olabilir .
Martijn


@ Shog9 Birleştirme burada cevapların yinelenen çabalar gibi görünmesini sağlar ve zaman damgaları hiç yardımcı olmaz .. Hala gitmek için yol.
nawfal

77

Agrega uzatma yöntemine baktınız mı?

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

23
Bu muhtemelen String.Join () 'den daha yavaştır ve kodda okumak daha zordur. Yine de "LINQ yolu" sorusuna cevap veriyor :-)
Chris Wenham

5
Evet, cevabı fikirlerimle lekelemek istemedim. : P
Robert

2
Aslında tartışmasız biraz daha yavaş. Toplama yerine bir StringBuilder ile Toplama kullanmak String.Join'den daha yavaştır.
Joel Mueller

4
10.000.000 iterasyonla bir test yapılmış, toplam 4.3 saniye ve string ile katılmıştır. 2.3 saniye sürmüştür. Bu yüzden, perf farkının yaygın kullanım vakalarının% 99'u için önemsiz olduğunu söyleyebilirim. Dolayısıyla, verilerinizi işlemek için zaten çok sayıda linq yapıyorsanız, genellikle bu güzel sözdizimini kırmanıza ve string.join imo'ya ihtiyacınız yoktur. gist.github.com/joeriks/5791981
joeriks


56

Kodumdan gerçek örnek:

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

Bir sorgu bir dize olan bir Name özelliği olan bir nesnedir ve seçilen listedeki tüm sorguların virgülle ayrılmış adlarını istiyorum.


2
Performans hakkındaki yorumlar göz önüne alındığında, örnek bir iletişim kutusu kapandığında bir kez çalışan koddan olduğunu ve listenin üzerinde yaklaşık on dizeden fazla olması muhtemel olmadığını eklemeliyim!
Daniel Earwicker

Linq to Entities de aynı görevi nasıl yapabilirim?
Binoj Antony

1
Mükemmel bir örnek. Bunu gerçek bir dünya senaryosuna eklediğiniz için teşekkür ederiz. Sonuçlandırılması gereken bir nesnenin özelliği ile aynı durumdaydım.
Jessy Houle

1
Listemin string özelliğini seçmenin ilk bölümünü bulmamda bana yardım ettiği için seçildi <T>
Nikki9696 20:11

1
Lütfen bu yaklaşımın performansı hakkında daha geniş bir dizi ile yazınız.
Giulio Caccin

31

Burada, diğer cevaplara ve benzer bir soruda ele alınan sorunlara (yani Toplama ve Birleştirme'nin 0 öğeyle başarısız olması) baktıktan sonra yerleştiğim birleştirilmiş Birleştirme / Linq yaklaşımı .

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

veya ( sdize değilse)

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

  • Basit
  • okuması ve anlaması kolay
  • jenerik elemanlar için çalışır
  • nesnelerin veya nesne özelliklerinin kullanılmasına izin verir
  • 0 uzunluklu elemanların durumunu ele alır
  • ek Linq filtreleme ile kullanılabilir
  • iyi performans gösterir (en azından tecrübelerime göre)
  • uygulamak için ek bir nesnenin (örn. StringBuilder) oluşturulmasını (manuel olarak) gerektirmez

Ve tabii ki Join bazen diğer yaklaşımlara ( for, foreach) gizlenen sinir bozucu son virgülle ilgilenir , bu yüzden ilk etapta bir Linq çözümü arıyordum.


1
eşleşmeyen parantez.
ctrl-alt-delor


3
Bu yanıtı beğendim, çünkü .Select()böyle kullanmak bu işlem sırasında her öğeyi değiştirmek için kolay bir yer sağlar. Örneğin, her öğeyi böyle bir karakterle sarmalamakstring Result = String.Join(",", split.Select(s => "'" + s + "'"));
Sam Storie

29

Sen kullanabilirsiniz StringBuilderin 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());

( SelectSadece daha fazla LINQ işi yapabileceğinizi göstermek için orada.)


2
+1 güzel. Ancak IMO, daha sonra silmekten daha fazla "," eklemekten kaçınmak için daha iyidir. Gibi bir şeynew[] {"one", "two", "three"}.Aggregate(new StringBuilder(), (sb, s) =>{if (sb.Length > 0) sb.Append(", ");sb.Append(s);return sb;}).ToString();
dss539

5
if (length > 0)Linq'i kontrol etmeyerek ve çıkararak değerli saat döngülerini kurtaracaksınız .
Binoj Antony

1
Dss539 ile hemfikirim. Benim new[] {"", "one", "two", "three"}.Aggregate(new StringBuilder(), (sb, s) => (String.IsNullOrEmpty(sb.ToString())) ? sb.Append(s) : sb.Append(", ").Append(s)).ToString();
sürümüm

22

3000'den fazla element ve StringBuilder vs Select & Aggregate durumu için hızlı performans verileri:

Birim testi - Süre (saniye)
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);
    }

Bunun için LINQ olmayan rotaya gitmeye karar vermede yardımcı oldu
crabCRUSHERclamCOLLECTOR

4
Saat farkı büyük olasılıkla + kullanarak StringBuilder vs String Concatination. LINQ veya Agrega ile ilgisi yoktur. StringBuilder'ı LINQ Aggregate'e (SO ile ilgili çok sayıda örnek) koyun ve bu kadar hızlı olmalıdır.
kontrol kutusu


12

' Süper havalı LINQ yolu ' ile, LINQ'nun işlevsel programlamayı genişletme yöntemlerinin kullanımı ile çok daha lezzetli hale getirme şeklinden bahsediyor olabilirsiniz. Demek istediğim, fonksiyonların iç içe yerleştirme yerine (diğerinin içinde) görsel olarak doğrusal bir şekilde (birbiri ardına) zincirlenmesine izin veren sözdizimsel şeker. Örneğin:

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

şöyle yazılabilir:

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

İkinci örneğin daha kolay okunmasını görebilirsiniz. Ayrıca, girintileme sorunlarından veya ifadenin sonunda görünen Lispy kapanış çözümleriyle daha fazla işlevin nasıl eklenebileceğini görebilirsiniz .

Diğer cevapların birçoğu, String.Joinbunun en hızlı veya en basit yol olduğu için gidilecek yol olduğunu belirtiyor. Ama eğer ' süper havalı LINQ yolu ' hakkındaki yorumumu alırsanız , cevap kullanmaktır, String.Joinancak fonksiyonlarınızı görsel olarak hoş bir şekilde zincirlemenize izin verecek bir LINQ tarzı uzatma yöntemine sarılmış olmalıdır. Yani yazmak sa.Concatenate(", ")istiyorsanız sadece böyle bir şey oluşturmanız gerekir:

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

Bu, doğrudan çağrı kadar performans gösteren kod sağlayacaktır (en azından algoritma karmaşıklığı açısından) ve bazı durumlarda özellikle bloktaki diğer kodlar zincirlenmiş fonksiyon stilini kullanıyorsa, kodu daha fazla okunabilir hale getirebilir (içeriğe bağlı olarak). .


1
Bu konudaki yazım hatası sayısı çılgınca: seperator => separator, Concatinate => Concatenate
SilverSideDown


5

Bu önceki soruda , kaynak olarak bir tamsayı dizisini hedefleyen, ancak genelleştirilmiş yanıtlar alan çeşitli alternatif cevaplar var.


5

Burada tek bir ifade olarak saf LINQ kullanıyor:

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));
}

Ve oldukça hızlı!


3

Biraz hile yapacağım ve bir yorumun içine yapışmak yerine burada her şeyin en iyisini özetleyen yeni bir cevap vereceğim.

Yani bunu bir satır yapabilirsiniz:

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); 

Düzenleme: Önce boş bir numaralandırılabilir olup olmadığını kontrol etmek veya .Replace("\a",string.Empty);ifadenin sonuna bir eklemek istersiniz . Sanırım biraz fazla zeki olmaya çalışıyordum.

@ A.friend'in cevabı biraz daha performanslı olabilir, Kaldır'ın yerine Replace'ın kaputun altında ne yaptığından emin değilim. \ A's ile biten dizeleri concat etmek istediğiniz bir sebep varsa, ayırıcılarınızı kaybedersiniz ... Bu durumda, aralarından seçim yapabileceğiniz başka süslü karakterleriniz var .


2

LINQ ve string.join()oldukça etkili bir şekilde birleştirebilirsiniz . Burada bir öğeyi bir dizeden kaldırıyorum. Bunu yapmanın daha iyi yolları var ama işte burada:

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


1

Burada bir sürü seçenek var. LINQ ve bir StringBuilder kullanabilirsiniz, böylece performansı da şöyle elde edebilirsiniz:

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();

builder.Length > 0ForEach'ta kontrol etmemek ve ForEach'tan sonraki ilk virgülü kaldırarak daha hızlı olurdu
Binoj Antony

1

Linq kullanarak bir IIS günlük dosyasını ayrıştırırken aşağıdaki hızlı ve kirli yaptım, 2 milyon satır çalışırken bellek yetersizliği var, ancak 1 milyon satır oldukça iyi çalıştı (15 saniye).

    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 kullanmamın gerçek nedeni, daha önce kullandığım bir Distinct () içindi:

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()
    ;


0

Bir süre önce bunun hakkında blog yazdım, dikişleri tam olarak aradığınız şey olarak yaptım:

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

Blog yazısında, IEnumerable üzerinde çalışan ve Concatenate olarak adlandırılan uzantı yöntemlerinin nasıl uygulanacağını açıklayın, bu aşağıdaki gibi şeyler yazmanıza izin verir:

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

Veya daha ayrıntılı şeyler:

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


Cevabı daha kolay anlamak için kodu burada birleştirebilir misiniz?
Giulio Caccin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.