ใช้ LINQ เพื่อเชื่อมสตริงเข้าด้วยกัน


346

วิธีที่มีประสิทธิภาพที่สุดในการเขียนโรงเรียนเก่าคืออะไร:

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?


1
คุณค้นพบวิธีการทำ LINQ ที่ยอดเยี่ยมอื่น ๆ
Robert S.

3
คำตอบที่เลือกไว้และตัวเลือกอื่น ๆ ทั้งหมดจะไม่ทำงานใน Linq to Entities
Binoj Antony

3
@Binoj Antony อย่าให้ฐานข้อมูลของคุณทำการต่อสายอักขระ
Amy B

6
@ Pr0fess0rX: เพราะมันทำไม่ได้และเพราะมันไม่ควร ฉันไม่รู้เกี่ยวกับฐานข้อมูลอื่น แต่ใน SQL Server คุณสามารถ concat (n) varcahr ซึ่ง จำกัด คุณให้ (n) varchar (สูงสุด) ไม่ควรเพราะตรรกะทางธุรกิจไม่ควรนำมาใช้ในชั้นข้อมูล
the_drow

ทางออกสุดท้ายที่มีซอร์สโค้ดเต็มรูปแบบและประสิทธิภาพสูงหรือไม่
Kiquenet

คำตอบ:


529

คำตอบนี้แสดงการใช้งาน 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);

ผลลัพธ์นี้:

, หนึ่งสองสาม

การรวมเป็นฟังก์ชันที่รับค่าและส่งคืนค่าสเกลาร์ ตัวอย่างจาก T-SQL ได้แก่ min, max และ sum ทั้ง VB และ C # ได้รับการสนับสนุนสำหรับการรวม ทั้ง VB และ C # รองรับการรวมเป็นวิธีการขยาย การใช้เครื่องหมายจุดจะเรียกใช้เมธอดบนวัตถุIEnumerable

จำไว้ว่าแบบสอบถามรวมจะดำเนินการทันที

ข้อมูลเพิ่มเติม - MSDN: การค้นหาแบบรวม


หากคุณต้องการAggregateใช้ตัวแปรการใช้ที่StringBuilderเสนอในข้อคิดเห็นโดยCodeMonkeyKingซึ่งจะเกี่ยวกับรหัสเดียวกันกับปกติString.Joinรวมถึงประสิทธิภาพที่ดีสำหรับวัตถุจำนวนมาก:

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

4
ตัวอย่างแรกไม่ส่งออก "หนึ่งสองสาม" ออก ", หนึ่ง, สอง, สาม" (สังเกตเครื่องหมายจุลภาคนำ)
Mort

ในตัวอย่างแรกของคุณตั้งแต่คุณเริ่มต้นด้วย""ค่าแรกที่ใช้currentเป็นสตริงว่าง ดังนั้นสำหรับองค์ประกอบอย่างน้อย 1 รายการคุณจะได้รับ, เมื่อเริ่มต้นสตริง
Michael Yanni

@ สั้นฉันได้แก้ไขสิ่งนี้แล้ว
sergtk

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

ใน. Net 4 มีการโอเวอร์โหลดใหม่สำหรับที่ยอมรับstring.Join IEnumerable<string>รหัสจะมีลักษณะดังนี้:

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

2
ตกลงดังนั้นวิธีการแก้ปัญหาไม่ได้ใช้ Linq แต่มันดูเหมือนว่าจะทำงานสวยดีกับฉัน
จ้าโรเบิร์ต

33
ToArray is linq :)
Amy B

18
นี่คือคำตอบที่ถูกต้องที่สุด มันเร็วกว่าทั้งคำถามและคำตอบที่ยอมรับและชัดเจนกว่าการรวมซึ่งต้องใช้คำอธิบายยาววรรคทุกครั้งที่มีการใช้
PRMan

@realPro เป็นเท็จโดยสิ้นเชิง github.com/microsoft/referencesource/blob/master/mscorlib/… บรรทัดที่ 161
Amy B

125

ทำไมต้องใช้ Linq

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

มันทำงานได้อย่างสมบูรณ์และยอมรับได้IEnumerable<string>เท่าที่ฉันจำได้ ไม่ต้องการAggregateอะไรที่นี่ซึ่งช้ากว่ามาก


19
การเรียนรู้ LINQ อาจจะเจ๋งและ LINQ อาจเป็นวิธีที่น่ารักในการทำให้จุดจบจบลง แต่การใช้ LINQ เพื่อให้ได้ผลลัพธ์ที่ได้จะไม่ดีถ้าพูดอย่างโง่เขลา
Jason Bunting

9
.NET 4.0 มีการโอเวอร์โหลด IEnumerable <string> และ IEnumrable <T> ซึ่งจะทำให้ใช้งานได้ง่ายขึ้นมาก
Cine

3
Cine ชี้ให้เห็นว่า. NET 4.0 มีการโอเวอร์โหลด รุ่นก่อนหน้าไม่ คุณยังสามารถString.Join(",", s.ToArray())ใช้เวอร์ชันเก่ากว่าได้
Martijn

1
FYI: ผสานจากstackoverflow.com/questions/122670/…
Shog9

@ Shog9 การผสานทำให้คำตอบที่นี่ดูเหมือนความพยายามซ้ำซ้อนและการประทับเวลาก็ไม่ช่วยเลย .. ยังคงเป็นไปได้
nawfal

77

คุณดูที่วิธีการรวม Aggregate หรือไม่

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

23
นั่นอาจช้ากว่า String.Join () และอ่านรหัสได้ยากขึ้น ไม่ตอบคำถามสำหรับ "วิธี LINQ" แม้ว่า :-)
คริสเฮม

5
ใช่ฉันไม่ต้องการมัวหมองคำตอบกับความคิดเห็นของฉัน : P
Robert S.

2
จริง ๆ แล้วมันค่อนข้างช้าลงอย่างแน่นอน แม้จะใช้การรวมกับ StringBuilder แทนการเรียงต่อกันจะช้ากว่า String.Join
Joel Mueller

4
ทำการทดสอบด้วยการทำซ้ำ 10.000.000 ซ้ำรวมใช้เวลา 4.3 วินาทีและ string.join ใช้เวลา 2.3 วินาที ดังนั้นฉันจะบอกว่า diff ต่างกันเล็กน้อยสำหรับ 99% ของกรณีการใช้งานทั่วไป ดังนั้นหากคุณกำลังทำ linq จำนวนมากเพื่อประมวลผลข้อมูลของคุณโดยปกติแล้วคุณไม่จำเป็นต้องทำลายไวยากรณ์ที่ดีและใช้ string.join imo gist.github.com/joeriks/5791981
joeriks

1
FYI: ผสานจากstackoverflow.com/questions/122670/…
Shog9

56

ตัวอย่างจริงจากรหัสของฉัน:

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

แบบสอบถามเป็นวัตถุที่มีคุณสมบัติชื่อซึ่งเป็นสตริงและฉันต้องการชื่อของแบบสอบถามทั้งหมดในรายการที่เลือกคั่นด้วยเครื่องหมายจุลภาค


2
ให้ความคิดเห็นเกี่ยวกับประสิทธิภาพการทำงานฉันควรเพิ่มว่าตัวอย่างมาจากรหัสที่ทำงานครั้งเดียวเมื่อปิดกล่องโต้ตอบและรายการไม่น่าจะมีมากกว่าสิบสายในนั้น!
Daniel Earwicker

เบาะแสใด ๆ ที่จะทำภารกิจเดียวกันนี้ใน Linq to Entities ได้อย่างไร
Binoj Antony

1
ตัวอย่างที่ยอดเยี่ยม ขอบคุณที่นำสิ่งนี้เข้าสู่สถานการณ์จริง ฉันมีสถานการณ์ที่แน่นอนเหมือนกันด้วยคุณสมบัติของวัตถุที่ต้องการการเชื่อมโยง
Jessy Houle

1
โหวตขึ้นเพื่อช่วยฉันคิดว่าส่วนแรกของการเลือกคุณสมบัติสตริงของรายการของฉัน <T>
Nikki9696

1
โปรดเขียนเกี่ยวกับประสิทธิภาพของการเข้าใกล้นี้ด้วยอาร์เรย์ที่กว้างขึ้น
Giulio Caccin

31

ต่อไปนี้เป็นวิธีเข้าร่วม / Linq แบบรวมที่ฉันตัดสินหลังจากดูคำตอบอื่น ๆ และปัญหาที่ระบุในคำถามที่คล้ายกัน (กล่าวคือการรวมและการต่อประสานล้มเหลวด้วย 0 องค์ประกอบ)

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

หรือ (ถ้าsไม่ใช่สตริง)

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

  • ง่าย
  • อ่านและเข้าใจง่าย
  • ใช้งานได้กับองค์ประกอบทั่วไป
  • อนุญาตให้ใช้วัตถุหรือคุณสมบัติของวัตถุ
  • จัดการกรณีขององค์ประกอบความยาว 0
  • สามารถใช้กับตัวกรอง Linq เพิ่มเติมได้
  • ทำงานได้ดี (อย่างน้อยก็ในประสบการณ์ของฉัน)
  • ไม่ต้องการการสร้างวัตถุเพิ่มเติม (เช่นStringBuilder) เพื่อนำไปใช้

และแน่นอนว่าการเข้าร่วมดูแลคอมม่าสุดท้ายที่น่ารำคาญซึ่งบางครั้งก็แอบเข้าไปในแนวทางอื่น ๆ ( for, foreach) ซึ่งเป็นสาเหตุที่ฉันกำลังมองหาโซลูชัน Linq ตั้งแต่แรก


1
เครื่องหมายวงเล็บที่พลาดไม่ได้
ctrl-alt-delor

1
FYI: ผสานจากstackoverflow.com/questions/122670/…
Shog9

3
ฉันชอบคำตอบนี้เพราะการใช้.Select()สิ่งนี้ทำให้ง่ายต่อการแก้ไขแต่ละองค์ประกอบในระหว่างการดำเนินการนี้ ตัวอย่างเช่นการห่อแต่ละรายการด้วยตัวละครบางตัวอย่างนั้นstring Result = String.Join(",", split.Select(s => "'" + s + "'"));
Sam Storie

29

คุณสามารถใช้StringBuilderใน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());

(ที่Selectอยู่ในนั้นเพียงเพื่อแสดงว่าคุณสามารถทำสิ่งต่างๆได้มากขึ้น LINQ)


2
+1 ดี อย่างไรก็ตาม IMO จะเป็นการดีกว่าที่จะหลีกเลี่ยงการเพิ่ม "," มากกว่าการลบในภายหลัง มีบางอย่างที่เหมือนกันnew[] {"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 และโดยการออก
Binoj Antony

1
ฉันเห็นด้วยกับ dss539 รุ่นของฉันอยู่ในแนวของnew[] {"", "one", "two", "three"}.Aggregate(new StringBuilder(), (sb, s) => (String.IsNullOrEmpty(sb.ToString())) ? sb.Append(s) : sb.Append(", ").Append(s)).ToString();
ProfNimrod

22

ข้อมูลประสิทธิภาพการทำงานอย่างรวดเร็วสำหรับกรณีของ StringBuilder vs Select & Aggregate มากกว่า 3,000 องค์ประกอบ:

การทดสอบหน่วย - ระยะเวลา (วินาที)
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);
    }

มีประโยชน์ในการตัดสินใจเลือกเส้นทางที่ไม่ใช่ LINQ สำหรับสิ่งนี้
crabCRUSHERclamCOLLECTOR

4
ความแตกต่างของเวลาน่าจะเป็น StringBuilder เทียบกับ String Concatination โดยใช้ + ไม่เกี่ยวข้องกับ LINQ หรือการรวม วาง StringBuilder ลงใน LINQ Aggregate (มีตัวอย่างมากมายให้กับ SO) และควรเร็วพอ
controlbox

16

ฉันมักจะใช้วิธีการขยาย:

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

5
string.Joinใน .net 4 แล้วสามารถใช้IEnumerable<T>สำหรับการใด ๆ Tโดยพล
เรียกซ้ำ

1
FYI: ผสานจากstackoverflow.com/questions/122670/…
Shog9

12

ด้วย ' สุดยอดวิธี LINQ ' คุณอาจกำลังพูดถึงวิธีที่ LINQ ทำให้การเขียนโปรแกรมใช้งานได้ดียิ่งขึ้นด้วยการใช้วิธีการขยาย ฉันหมายถึงน้ำตาล syntactic ที่ช่วยให้ฟังก์ชั่นถูกล่ามโซ่ด้วยวิธีการมองเห็นเป็นเส้นตรง (ต่อเนื่องกัน) แทนที่จะทำรัง ตัวอย่างเช่น:

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

สิ่งนี้จะให้รหัสที่มีประสิทธิภาพเท่ากับการโทรโดยตรง (อย่างน้อยก็ในแง่ของความซับซ้อนของอัลกอริทึม) และในบางกรณีอาจทำให้โค้ดอ่านง่ายขึ้น (ขึ้นอยู่กับบริบท) โดยเฉพาะอย่างยิ่งหากรหัสอื่น ๆ ในบล็อกใช้ .


1
จำนวนการพิมพ์ผิดในเธรดนี้เป็นบ้า: seperator => separator, Concatinate => Concatenate
SilverSideDown

1
FYI: ผสานจากstackoverflow.com/questions/122670/…
Shog9


5

นี่คือการใช้ 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));
}

และมันก็เร็วมากเลยทีเดียว!


3

ฉันจะโกงนิดหน่อยและทิ้งคำตอบใหม่ให้กับสิ่งนี้ซึ่งดูเหมือนว่าจะสรุปสิ่งที่ดีที่สุดของทุกอย่างที่นี่แทนที่จะติดไว้ในความคิดเห็น

ดังนั้นคุณสามารถหนึ่งบรรทัดนี้:

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 คุณจะเสียตัวคั่น ... ฉันคิดว่าไม่น่าจะเป็นไปได้ หากเป็นเช่นนั้นคุณมีตัวละครแฟนซีอื่น ๆให้เลือก


2

คุณสามารถรวม LINQ และstring.join()ค่อนข้างมีประสิทธิภาพ ที่นี่ฉันจะลบรายการจากสตริง มีวิธีที่ดีกว่าในการทำเช่นนี้ แต่นี่คือ:

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

1
FYI: ผสานจากstackoverflow.com/questions/122670/…
Shog9

1

ตัวเลือกมากมายที่นี่ คุณสามารถใช้ 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();

มันจะเร็วกว่าหากไม่ตรวจสอบbuilder.Length > 0ใน ForEach และลบเครื่องหมายจุลภาคแรกหลังจาก ForEach
Binoj Antony

1

ฉันทำสิ่งต่อไปนี้อย่างรวดเร็วและสกปรกเมื่อแยกวิเคราะห์ไฟล์บันทึก IIS โดยใช้ linq มันใช้งานได้ @ 1 ล้านบรรทัดค่อนข้างดี (15 วินาที) แม้ว่าจะมีข้อผิดพลาดหน่วยความจำไม่เพียงพอเมื่อลอง 2 ล้านบรรทัด

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

1
FYI: ผสานจากstackoverflow.com/questions/122670/…
Shog9

0

ฉัน blogged เกี่ยวกับเรื่องนี้ในขณะที่สิ่งที่ฉันไม่ตะเข็บเพื่อเป็นสิ่งที่คุณกำลังมองหา:

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

1
FYI: ผสานจากstackoverflow.com/questions/122670/…
Shog9

คุณสามารถต่อรหัสเข้าที่นี่เพื่อให้คำตอบนั้นง่ายกว่าที่จะเข้าใจหรือไม่?
Giulio Caccin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.