รวมสองรายการ (หรือมากกว่า) เป็นหนึ่งเดียวใน C # .NET


246

เป็นไปได้หรือไม่ที่จะแปลงสองรายการขึ้นไปเป็นรายการเดียวใน. NET โดยใช้ C #

ตัวอย่างเช่น,

public static List<Product> GetAllProducts(int categoryId){ .... }
.
.
.
var productCollection1 = GetAllProducts(CategoryId1);
var productCollection2 = GetAllProducts(CategoryId2);
var productCollection3 = GetAllProducts(CategoryId3);

คุณต้องการรวมผลิตภัณฑ์การรวบรวม 1 2 และ 3 หรือไม่

คุณหมายถึงรวมมากกว่าหนึ่งรายการในหนึ่งรายการ?
Amr Badawy

ตัวอย่างของคุณเป็นความสับสนฉัน ... คุณกำลังมองหาAddRange-Method?
Bobby

คำตอบ:


457

คุณสามารถใช้ LINQ ConcatและToListวิธีการ:

var allProducts = productCollection1.Concat(productCollection2)
                                    .Concat(productCollection3)
                                    .ToList();

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

var allProducts = new List<Product>(productCollection1.Count +
                                    productCollection2.Count +
                                    productCollection3.Count);
allProducts.AddRange(productCollection1);
allProducts.AddRange(productCollection2);
allProducts.AddRange(productCollection3);

( AddRangeใส่กล่องพิเศษICollection<T>เพื่อประสิทธิภาพ)

ฉันจะไม่ใช้วิธีนี้เว้นแต่คุณจะต้องทำจริงๆ


4
BTW productCollection1.ForEach(p => allProducts.Add(p))นั้นมีประสิทธิภาพมากกว่า AddRange
Marc Climent

8
@MarcCliment: นั่นเป็นคำสั่งแบบครอบคลุมที่ค่อนข้างหนา - โดยเฉพาะอย่างยิ่งเมื่อAddRangeทำสำเนาบล็อกจากอาร์เรย์หนึ่งไปยังอีกชุดหนึ่ง คุณมีลิงค์ใด ๆ เพื่อเป็นหลักฐานในเรื่องนี้หรือไม่?
Jon Skeet

4
@ MacCliment: สำหรับสิ่งหนึ่งในการทดสอบของคุณคุณไม่ได้สร้างรายการที่มีขนาดสุดท้ายที่ถูกต้อง - ในขณะที่รหัสในคำตอบของฉันไม่ ทำอย่างนั้นและการทดสอบ 10000 * 10000 นั้นเร็วขึ้นโดยใช้ AddRange อย่างน้อยที่สุด - แม้ว่าผลลัพธ์อื่น ๆ จะไม่สอดคล้องกัน (คุณควรบังคับให้มีการรวบรวมขยะระหว่างการทดสอบ - และฉันขอยืนยันว่าการทดสอบสั้น ๆ นั้นเล็กนิดเดียว)
Jon Skeet

6
@MarcCliment: กรณีใดเป็นพิเศษ CLR ไหน สถาปัตยกรรม CPU ใด ในเครื่องของฉันฉันได้ผลลัพธ์หลายอย่าง นี่คือเหตุผลที่ฉันเกลียดที่จะรัฐ / ยอมรับข้อความสั่งผ้าห่มเช่น "BTW productionCollection1.ForEach(...)มีประสิทธิภาพมากกว่า AddRange" การอธิบายนั้นไม่ค่อยมีประสิทธิภาพมากนัก ฉันกำลังประหลาดใจว่า AddRange ไม่ได้ตีมันอย่างคล่องแคล่วแม้ว่า - ฉันจำเป็นต้องตรวจสอบว่าต่อไป
Jon Skeet

15
ฉันได้อัปเดตส่วนที่สำคัญ ( gist.github.com/mcliment/4690433 ) ด้วยการทดสอบประสิทธิภาพโดยใช้ BenchmarksDotNet และทดสอบอย่างถูกต้องAddRangeเป็นตัวเลือกที่ดีที่สุดสำหรับความเร็วดิบ (ประมาณ 4x และยิ่งใหญ่กว่ารายการยิ่งดี) ขณะที่จอน suggeste
Marc Climent

41

สมมติว่าคุณต้องการรายการที่มีทั้งหมดของผลิตภัณฑ์สำหรับการระบุหมวดหมู่หมายเลขคุณสามารถรักษาแบบสอบถามของคุณเป็นประมาณการตามด้วยแฟบการดำเนินงาน มีตัวดำเนินการ LINQ ที่ทำเช่นนั้น:SelectMany .

// implicitly List<Product>
var products = new[] { CategoryId1, CategoryId2, CategoryId3 }
                     .SelectMany(id => GetAllProducts(id))
                     .ToList();

ใน C # 4 คุณสามารถย่อ SelectMany ไปที่: .SelectMany(GetAllProducts)

หากคุณมีรายการที่แสดงถึงผลิตภัณฑ์สำหรับแต่ละ Id แล้วสิ่งที่คุณต้องการคือการต่อข้อมูลตามที่คนอื่น ๆ ชี้ให้เห็น


8
หาก OP ไม่ต้องการรายชื่อบุคคลด้วยเหตุผลอื่นนี่เป็นทางออกที่ยอดเยี่ยม
Jon Skeet

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

22

คุณสามารถรวมเข้าด้วยกันโดยใช้ LINQ:

  list = list1.Concat(list2).Concat(list3).ToList();

วิธีการใช้แบบดั้งเดิมที่มากขึ้นList.AddRange()อาจมีประสิทธิภาพมากกว่า





4

ฉันรู้ว่านี่เป็นคำถามเก่าที่ฉันคิดว่าฉันอาจเพิ่ม 2 เซ็นต์ของฉัน

หากคุณมีList<Something>[]คุณสามารถเข้าร่วมได้โดยใช้Aggregate

public List<TType> Concat<TType>(params List<TType>[] lists)
{
    var result = lists.Aggregate(new List<TType>(), (x, y) => x.Concat(y).ToList());

    return result;
}

หวังว่านี่จะช่วยได้


2

ฉันได้แสดงความคิดเห็นแล้ว แต่ฉันยังคิดว่าเป็นตัวเลือกที่ถูกต้องเพียงแค่ทดสอบว่าในสภาพแวดล้อมของคุณดีกว่าวิธีหนึ่งหรืออีกวิธีหนึ่ง ในกรณีเฉพาะของฉันการใช้งานsource.ForEach(p => dest.Add(p))มีประสิทธิภาพดีกว่าแบบคลาสสิกAddRangeแต่ฉันไม่ได้ตรวจสอบสาเหตุที่ระดับต่ำ

คุณสามารถดูรหัสตัวอย่างได้ที่นี่: https://gist.github.com/mcliment/4690433

ดังนั้นตัวเลือกจะเป็น:

var allProducts = new List<Product>(productCollection1.Count +
                                    productCollection2.Count +
                                    productCollection3.Count);

productCollection1.ForEach(p => allProducts.Add(p));
productCollection2.ForEach(p => allProducts.Add(p));
productCollection3.ForEach(p => allProducts.Add(p));

ทดสอบเพื่อดูว่าเหมาะกับคุณหรือไม่

ข้อจำกัดความรับผิดชอบ : ฉันไม่ได้สนับสนุนการแก้ไขปัญหานี้ฉันพบว่าConcatชัดเจนที่สุด ฉันเพิ่งพูด - ในการสนทนากับจอน - ในเครื่องของฉันกรณีนี้ทำงานได้ดีกว่าAddRangeแต่เขาบอกว่ามีความรู้มากกว่าฉันว่านี่ไม่สมเหตุสมผล มีส่วนสำคัญถ้าคุณต้องการเปรียบเทียบ


ไม่ว่าคุณจะถกเถียงกับ Jon Skeet ในความคิดเห็นใดฉันชอบความสะอาดที่เสนอโดยวิธีแก้ปัญหาที่เสนอของเขานี่เป็นเพียงการเพิ่มการตีความที่ไม่จำเป็นตามเจตนาของรหัส
Vix

1
ฉันคิดว่าตัวเลือกที่ชัดเจนที่สุดคือ Concat ซึ่งเป็นลักษณะทั่วไปของ AddRange ความหมายถูกต้องมากขึ้นและไม่ได้แก้ไขรายการในสถานที่ซึ่งทำให้สามารถโยงได้ การสนทนากับจอนนั้นเกี่ยวกับประสิทธิภาพไม่ใช่ความสะอาด
Marc Climent

ฉันมีสิ่งนี้: var allProducts = lstName.Concat(lstCMSID) .Concat(lstSpecialtyPhys) .ToList();ซึ่งเพิ่มลงใน GridView แต่เป็นหนึ่งคอลัมน์ ฉันต้องการแยกพวกมันออกเป็นสามคอลัมน์
Si8

2
// I would make it a little bit more simple

 var products = new List<List<product>> {item1, item2, item3 }.SelectMany(id => id).ToList();

วิธีนี้เป็นรายการที่มีหลายมิติและ. SelectMany () จะทำให้แบนเป็นผลิตภัณฑ์จำนวนนับไม่ถ้วนแล้วฉันจะใช้วิธี. ToList () หลังจากนั้น


2

เพื่อรวมหรือรวมเข้ากับรายการในรายการเดียว

  • มีสิ่งหนึ่งที่ต้องเป็นจริง: ประเภทของรายการทั้งสองจะเท่ากัน

  • ตัวอย่างเช่น : ถ้าเรามีรายการstringเราจึงสามารถเพิ่มเพิ่มรายการอื่นลงในรายการที่มีอยู่ซึ่งมีรายการประเภทสตริงมิฉะนั้นเราจะไม่สามารถทำได้

ตัวอย่าง :

class Program
{
   static void Main(string[] args)
   {
      List<string> CustomerList_One = new List<string> 
      {
         "James",
         "Scott",
         "Mark",
         "John",
         "Sara",
         "Mary",
         "William",
         "Broad",
         "Ben",
         "Rich",
         "Hack",
         "Bob"
      };

      List<string> CustomerList_Two = new List<string> 
      {
         "Perter",
         "Parker",
         "Bond",
         "been",
         "Bilbo",
         "Cooper"
      };

      // Adding all contents of CustomerList_Two to CustomerList_One.
      CustomerList_One.AddRange(CustomerList_Two);

      // Creating another Listlist and assigning all Contents of CustomerList_One.
      List<string> AllCustomers = new List<string>();

      foreach (var item in CustomerList_One)
      {
         AllCustomers.Add(item);
      }

      // Removing CustomerList_One & CustomerList_Two.
      CustomerList_One = null;

      CustomerList_Two = null;
      // CustomerList_One & CustomerList_Two -- (Garbage Collected)
      GC.Collect();

      Console.WriteLine("Total No. of Customers : " +  AllCustomers.Count());
      Console.WriteLine("-------------------------------------------------");
      foreach (var customer in AllCustomers)
      {
         Console.WriteLine("Customer : " + customer);
      }
      Console.WriteLine("-------------------------------------------------");

   }
}

1

ในกรณีพิเศษ: "องค์ประกอบทั้งหมดของ List1 ไปที่ List2 ใหม่": (เช่นรายการสตริง)

List<string> list2 = new List<string>(list1);

ในกรณีนี้list2จะถูกสร้างขึ้นด้วยองค์ประกอบทั้งหมดจาก list1



0

เมื่อคุณมีรายการน้อย แต่คุณไม่ทราบจำนวนที่แน่นอนให้ใช้สิ่งนี้:

listsOfProducts มีรายการไม่กี่เต็มไปด้วยวัตถุ

List<Product> productListMerged = new List<Product>();

listsOfProducts.ForEach(q => q.ForEach(e => productListMerged.Add(e)));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.