여러 요소 컬렉션을 결합하는 우아한 방법?


94

각각 동일한 유형 (예 : List<int> fooList<int> bar)의 개체를 포함하는 임의의 수의 컬렉션이 있다고 가정 해 보겠습니다 . 이러한 컬렉션이 컬렉션에 포함 된 경우 (예 : 유형의 List<List<int>>) SelectMany모든 컬렉션을 하나의 컬렉션으로 결합하는 데 사용할 수 있습니다 .

그러나 이러한 컬렉션이 아직 동일한 컬렉션에없는 경우 다음과 같은 메서드를 작성해야한다는 느낌이 듭니다.

public static IEnumerable<T> Combine<T>(params ICollection<T>[] toCombine)
{
   return toCombine.SelectMany(x => x);
}

나는 다음과 같이 부를 것입니다.

var combined = Combine(foo, bar);

Combine위와 같은 유틸리티 메서드를 작성하지 않고도 (여러 개의) 컬렉션을 결합하는 깨끗하고 우아한 방법이 있습니까? LINQ에서 할 수있는 방법이 있어야 할만큼 간단 해 보이지만 아닐 수도 있습니다.



매우 많은 수의 열거 형을 연결하기 위해 Concat에주의하십시오. 스택을 날려 버릴 수 있습니다. programmaticallyspeaking.com/…
nawfal

답변:


107

LINQ를 찾고 .Concat()계십니까?

var combined = foo.Concat(bar).Concat(foobar).Concat(...);

또는 .Union()중복 요소를 제거합니다.


2
감사; 내가 처음에 이것을하지 않은 유일한 이유는 (나에게) 다루어야 할 컬렉션이 많을수록 더 추악 해지는 것 같습니다. 그러나 이것은 미래의 개발자가 이미 익숙 할 것 같은 기존 LINQ 함수를 사용하는 이점이 있습니다.
Donut

2
.Union()팁 주셔서 감사합니다 . 곰 염두에 당신이 필요합니다 그것이 경우에 대비하여 사용자 정의 유형에 IComparer를 구현합니다.
Gonzo345

29

나에게 Concat확장 메서드로서 에게 연결하는 큰 시퀀스가 ​​여러 개있을 때 코드에서 그다지 우아하지 않습니다. 이것은 코드 들여 쓰기 / 서식 문제 일 뿐이며 매우 개인적인 문제입니다.

물론 다음과 같이 보입니다.

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

다음과 같이 읽을 때 그렇게 읽을 수 없습니다.

var list = list1.Select(x = > x)
   .Concat(list2.Where(x => true)
   .Concat(list3.OrderBy(x => x));

또는 다음과 같은 경우 :

return Normalize(list1, a, b)
    .Concat(Normalize(list2, b, c))
       .Concat(Normalize(list3, c, d));

또는 원하는 형식이 무엇이든. 더 복잡한 연결을 사용하면 상황이 악화됩니다. 나의 이유위의 스타일에 일종의 인지 부조화 는 첫 번째 시퀀스는 Concat방법 외부에 있고 후속 시퀀스는 내부에 있기 때문입니다. 오히려 Concat확장 스타일이 아닌 정적 메서드를 직접 호출하는 것을 선호합니다 .

var list = Enumerable.Concat(list1.Select(x => x),
                             list2.Where(x => true));

더 많은 수의 시퀀스 연결을 위해 OP에서와 동일한 정적 방법을 수행합니다.

public static IEnumerable<T> Concat<T>(params IEnumerable<T>[] sequences)
{
    return sequences.SelectMany(x => x);
}

그래서 다음과 같이 쓸 수 있습니다.

return EnumerableEx.Concat
(
    list1.Select(x = > x),
    list2.Where(x => true),
    list3.OrderBy(x => x)
);

더 좋아 보인다. 내가 작성해야하는 여분의 중복 클래스 이름은 내 시퀀스가 Concat호출로 더 깨끗해 보이는 것을 고려할 때 문제가되지 않습니다 . C # 6 에서는 문제가 적습니다. . 다음과 같이 작성할 수 있습니다.

return Concat(list1.Select(x = > x),
              list2.Where(x => true),
              list3.OrderBy(x => x));

C #에서 다음과 같은 목록 연결 연산자가 있기를 바랍니다.

list1 @ list2 // F#
list1 ++ list2 // Scala

그렇게하면 훨씬 더 깨끗해집니다.


4
코딩 스타일에 대한 관심에 감사드립니다. 이것은 훨씬 더 우아합니다.
Bryan Rayner

아마도 귀하의 답변의 더 작은 버전이 여기에서 상위 답변과 병합 될 수 있습니다.
Bryan Rayner

2
이뿐만 아니라 포맷과 같은 I - I (예 : 개별 목록 작업을 수행하는 것을 선호하지만 Where, OrderBy명확성을 위해 첫번째), 특히이 예보다 더 복잡들이있는 거 아무것도 경우.
brichins

27

당신이 경우에 즉, 컬렉션의 컬렉션을 가지고 List<List<T>>, Enumerable.Aggregate하나에 모든 목록을 결합하는 더 우아한 방법입니다 :

var combined = lists.Aggregate((acc, list) => { return acc.Concat(list); });

1
어떻게 lists먼저 여기에 도착 합니까? 이것이 OP가 가진 첫 번째 문제입니다. 그가 collection<collection>시작 해야한다면 SelectMany훨씬 간단합니다.
nawfal

무슨 일이 있었는지 확실하지 않지만 이것은 잘못된 질문에 대한 대답 인 것 같습니다. 이를 반영하기 위해 답변을 수정했습니다. 많은 사람들이 필요 목록 <리스트 <T >> 결합하기 때문에 여기까지 보인다 않습니다
astreltsov



7

항상 Concat과 결합 된 Aggregate를 사용할 수 있습니다.

        var listOfLists = new List<List<int>>
        {
            new List<int> {1, 2, 3, 4},
            new List<int> {5, 6, 7, 8},
            new List<int> {9, 10}
        };

        IEnumerable<int> combined = new List<int>();
        combined = listOfLists.Aggregate(combined, (current, list) => current.Concat(list)).ToList();

1
지금까지 최고의 솔루션입니다.
Oleg

@Oleg SelectMany는 더 간단합니다.
nawfal 2019

6

내가 보는 유일한 방법은 Concat()

 var foo = new List<int> { 1, 2, 3 };
 var bar = new List<int> { 4, 5, 6 };
 var tor = new List<int> { 7, 8, 9 };

 var result = foo.Concat(bar).Concat(tor);

그러나 더 나은 것을 결정해야합니다.

var result = Combine(foo, bar, tor);

또는

var result = foo.Concat(bar).Concat(tor);

더 나은 선택 이되는 이유Concat() 중 하나 는 다른 개발자에게 더 분명 할 것이라는 점입니다. 더 읽기 쉽고 간단합니다.


3

다음과 같이 Union을 사용할 수 있습니다.

var combined=foo.Union(bar).Union(baz)...

그래도 동일한 요소가 제거되므로 해당 요소가있는 경우 Concat대신 을 사용할 수 있습니다 .


4
아니! Enumerable.Union원하는 결과를 생성하지 않는 집합 공용체입니다 (한 번만 중복 생성됨).
jason

예, 개체에 대해 몇 가지 특별한 처리를해야하므로 개체의 특정 인스턴스가 필요합니다. int내 예에서 사용하면 사람들이 약간 멀어 졌을 수 있습니다. 하지만 Union이 경우에는 작동하지 않습니다.
Donut

2

컬렉션 이니셜 라이저를 사용하는 몇 가지 기술-

다음 목록을 가정합니다.

var list1 = new List<int> { 1, 2, 3 };
var list2 = new List<int> { 4, 5, 6 };

SelectMany 배열 이니셜 라이저로 (정말 우아하지는 않지만 도우미 함수에 의존하지 않음) :

var combined = new []{ list1, list2 }.SelectMany(x => x);

이니셜 라이저IEnumerable<T> 에서 허용하는 Add에 대한 목록 확장을 정의합니다 .List<T>

public static class CollectionExtensions
{
    public static void Add<T>(this ICollection<T> collection, IEnumerable<T> items)
    {
        foreach (var item in items) collection.Add(item);
    }
}

그런 다음 이와 같은 다른 요소의 요소를 포함하는 새 목록을 만들 수 있습니다 (단일 항목을 혼합 할 수도 있음).

var combined = new List<int> { list1, list2, 7, 8 };

1

별도의 컬렉션으로 시작한다는 점을 감안할 때 솔루션이 다소 우아하다고 생각합니다. 당신은 그들을 함께 꿰매기 위해 무언가 를해야 할 것입니다.

어디에서나 사용할 수 있도록 Combine 메서드에서 확장 메서드를 만드는 것이 구문 적으로 더 편리 할 것입니다.


저는 확장 메서드 아이디어가 마음에 들었습니다. LINQ에 이미 동일한 작업을 수행 할 메서드가있는 경우 휠을 재발 명하고 싶지 않았습니다 (그리고 다른 개발자도 즉시 이해할 수 있음)
Donut

1

필요한 것은 다음과 IEnumerable<IEnumerable<T>> lists같습니다.

var combined = lists.Aggregate((l1, l2) => l1.Concat(l2));

이 모든 항목을 결합합니다 lists하나에 IEnumerable<T>(중복 포함). 다른 답변에서 언급했듯이 Union대신 사용 Concat하여 중복을 제거하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.