C # .NET에서 두 개 이상의 목록을 하나로 병합


246

C #을 사용하는 .NET에서 둘 이상의 목록을 하나의 단일 목록으로 변환 할 수 있습니까?

예를 들어

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

productCollection1 2와 3을 병합 하시겠습니까?

하나의 목록에 둘 이상의 목록을 병합한다는 의미입니까?
Amr Badawy

귀하의 예가 혼란 스럽습니다 ...- 방법을 찾고 AddRange있습니까?
Bobby

답변:


457

LINQ ConcatToList메소드를 사용할 수 있습니다 .

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

( 효율성 AddRangeICollection<T>위해 특수한 경우 입니다.)

당신이 정말로해야하지 않는 한이 접근법을 취하지 않을 것입니다.


4
BTW productCollection1.ForEach(p => allProducts.Add(p))는 AddRange보다 성능이 뛰어납니다.
Marc Climent

8
@MarcCliment : 이것은 AddRange하나의 기본 배열에서 다른 배열로 블록 복사를 수행 할 때 상당히 대담한 담요 입니다. 이 증거에 대한 링크가 있습니까?
Jon Skeet

4
@MarcCliment : 음은 한 가지, 당신의 검사 결과에 당신이있어 하지 올바른 최종 크기로 목록을 작성 - 내 대답의 코드가 못하다. 다른 결과가 일치하지 않더라도 AddRange를 사용하면 10000 * 10000 테스트가 더 빨라집니다. (당신은 또한 테스트 사이의 가비지 컬렉션을 강제해야한다 - 그리고 나는 매우 짧은 테스트가 의미없이 작은 주장 것입니다.)
존 소총을

6
@MarcCliment : 어떤 특별한 경우? 어떤 CLR? 어떤 CPU 아키텍처? 내 컴퓨터에서 결과가 혼합되어 나타납니다. 이것이 바로 "BTW, productionCollection1.ForEach(...)AddRange보다 성능이 뛰어나다 "와 같은 포괄적 인 진술을 진술 / 수락하는 것을 싫어하는 이유 입니다. 성능이 그렇게 쉽게 설명되는 경우는 거의 없습니다. 내가 하고 내가 그 더 조사 할 필요가 -하지만 AddRange가 솜씨를 치고 있지 않은지 놀라게했다.
Jon Skeet 2013

15
Jon으로 벤치 마크 DotNet을 사용하여 성능 테스트로 요점 ( gist.github.com/mcliment/4690433 )을 업데이트 하고 올바르게 테스트 AddRange했으며 원시 속도 (약 4 배 및 목록이 클수록 증가하는 것이 가장 좋습니다)에 가장 적합한 옵션입니다 암시합니다.
Marc Climent

41

지정된 category-Id에 대한 모든 제품이 포함 된 목록을 원한다고 가정하면 쿼리를 프로젝션으로 처리 한 다음 병합 작업을 수행 할 수 있습니다. 이를 수행하는 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()이 더 효율적일 수 있습니다.



8

Concat 확장 방법을 사용할 수 있습니다 .

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


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가장 분명한 해결책을 찾습니다 . 방금 Jon과의 토론에서이 경우는이 경우보다 성능이 AddRange뛰어나지 만 나보다 훨씬 많은 지식을 가지고 있으면 이것이 의미가 없다고 말합니다. 비교하고 싶다면 요점이 있습니다.


주석에서 Jon Skeet과의 토론에 관계없이 제안 된 솔루션이 제공하는 청결 함을 선호합니다. 이는 코드의 의도에 대한 불필요한 해석을 추가합니다.
Vix

1
가장 명확한 옵션은 Concat이 AddRange의 일반화로서 의미 적으로 더 정확하고 목록을 제자리에서 수정하여 체인화 할 수는 없다고 생각합니다. Jon과의 논의는 청결이 아니라 성능에 관한 것이 었습니다.
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 ()는 그것을 IEnumerable 제품으로 병합 한 다음 .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.