linq 결과를 HashSet 또는 HashedSet로 변환하는 방법


196

ISet 인 클래스에 속성이 있습니다. linq 쿼리 결과를 해당 속성으로 가져 오려고하지만 그렇게하는 방법을 알 수 없습니다.

기본적으로 이것의 마지막 부분을 찾으십시오.

ISet<T> foo = new HashedSet<T>();
foo = (from x in bar.Items select x).SOMETHING;

또한 이것을 할 수 있습니다 :

HashSet<T> foo = new HashSet<T>();
foo = (from x in bar.Items select x).SOMETHING;

나는 당신이 그 HashedSet부분을 배제해야한다고 생각합니다 . 그냥 이후 혼란 C#LINQ라는 아무것도하지 않습니다 HashedSet.
Jogge

답변은 질문 자체가 아닌 답변 상자에 들어갑니다. SO 규칙으로 완벽하게 맞는 자신의 질문에 대답하고 싶다면 그에 대한 답변을 작성하십시오.
Adriaan

답변:


308

나는 이것을하는 내장 된 것이 없다고 생각 하지만 확장 방법을 작성하는 것은 정말 쉽습니다.

public static class Extensions
{
    public static HashSet<T> ToHashSet<T>(
        this IEnumerable<T> source,
        IEqualityComparer<T> comparer = null)
    {
        return new HashSet<T>(source, comparer);
    }
}

T명시 적으로 유형을 표현할 수 없기 때문에 확장 방법 (또는 적어도 어떤 형태의 일반적인 방법)을 원합니다 .

var query = from i in Enumerable.Range(0, 10)
            select new { i, j = i + 1 };
var resultSet = query.ToHashSet();

당신은 그것을 명시 적으로 호출하여 그것을 할 수 없습니다 HashSet<T>생성자에 . 우리는 일반적인 방법으로 타입을 추론하고 있습니다.

이제 이름을 지정 하고 반환 할 있습니다. 하지만 구체적인 유형을 고수 합니다. 이는 표준 LINQ 연산자 ( , )와 일치하며 향후 확장 (예 :)을 허용합니다 . 사용할 비교를 지정하여 과부하를 제공 할 수도 있습니다.ToSetISet<T>ToHashSetToDictionaryToListToSortedSet


2
익명 유형에 대한 좋은 지적. 그러나 익명 형식에는 GetHashCodeEquals? 그렇지 않으면 HashSet에서 그다지 좋지 않을 것입니다 ...
Joel Mueller

4
@Joel : 그렇습니다. 그렇지 않으면 모든 종류의 일들이 실패 할 것입니다. 분명히 그들은 구성 요소 유형에 대해 기본 동등 비교자를 사용하지만 일반적으로 충분합니다.
Jon Skeet

2
@ JonSkeet-ToDictionary 및 ToList와 함께 표준 LINQ 연산자에 이것이 제공되지 않는 이유가 있습니까? 나는 그런 일이 생략 이유를 종종 좋은 이유가 있다는 것을 들었습니다 누락을 IEnumerable <T> .ForEach의 방법과 유사
스티븐 홀트

1
@StephenHolt :에 대한 저항에 동의 ForEach하지만 ToHashSet훨씬 더 의미 ToHashSet가 있습니다. 일반적인 "유용성 표시 줄을 충족하지 않습니다"(나는 동의하지 않지만 ...)
Jon Skeet

1
@Aaron : 확실하지 않습니다. 아마도 LINQ-to-Objects 공급자가 간단하지 않기 때문일까요? (나는 그것이 실현 불가능할 것이라고 상상할 수 없다.)
Jon Skeet

75

IEnumerable을 HashSet의 생성자로 전달하십시오.

HashSet<T> foo = new HashSet<T>(from x in bar.Items select x);

2
익명 유형의 결과에 어떻게 대처할 것입니까?
Jon Skeet

7
@ Jon, 나는 달리 확인 될 때까지 YAGNI라고 가정합니다.
Anthony Pegram

1
분명히 나는 ​​아니에요. 다행히도이 특정 예에서는 필요하지 않습니다.
Joel Mueller

@Jon 유형은 OP에 의해 지정됩니다 (첫 번째 줄은 컴파일러가 아닙니다)이 경우 나는 지금 YAGNI에 동의해야합니다.
Rune FS

2
@Rune FS :이 경우 샘플 코드가 현실적이지 않은 것 같습니다. 형식 매개 변수 T를 갖는 것은 매우 드물지만의 각 항목은 bar.Items입니다 T. 이 일반적인 목적을 달성하는 것이 얼마나 쉬운 지, LINQ에서 익명 유형이 얼마나 자주 등장 하는지를 감안할 때 추가 조치를 취할 가치가 있다고 생각합니다.
Jon Skeet 21:06에


19

@Joel이 말했듯이 열거 형을 전달할 수 있습니다. 확장 방법을 원한다면 다음을 수행 할 수 있습니다.

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> items)
{
    return new HashSet<T>(items);
}

3

세트에 대한 읽기 전용 액세스가 필요하고 소스가 메소드의 매개 변수 인 경우

public static ISet<T> EnsureSet<T>(this IEnumerable<T> source)
{
    ISet<T> result = source as ISet<T>;
    if (result != null)
        return result;
    return new HashSet<T>(source);
}

그 이유는 사용자가 ISet이미 메소드를 호출하여 사본을 작성할 필요가 없기 때문입니다.


3

닷넷 프레임 워크와 .NET의 핵심에 확장 방법 빌드로 변환하기위한이 IEnumerableA를 HashSet: https://docs.microsoft.com/en-us/dotnet/api/?term=ToHashSet

public static System.Collections.Generic.HashSet<TSource> ToHashSet<TSource> (this System.Collections.Generic.IEnumerable<TSource> source);

아직 .NET 표준 라이브러리에서 사용할 수없는 것 같습니다 (작성 당시). 그런 다음이 확장 방법을 사용합니다.

    [Obsolete("In the .NET framework and in NET core this method is available, " +
              "however can't use it in .NET standard yet. When it's added, please remove this method")]
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source, IEqualityComparer<T> comparer = null) => new HashSet<T>(source, comparer);

2

꽤 간단합니다 :)

var foo = new HashSet<T>(from x in bar.Items select x);

그리고 예 T는 OP에 의해 지정된 유형입니다 :)


유형 인수를 지정하지 않습니다.
Jon Skeet

오늘의 가장 가혹한 투표권이 된 Lol :). 나에게 지금 정확히 같은 정보가 있습니다. 타입 추론 시스템이 이미 그 타입을 아직 추론하지 않는 이유는 무엇입니까? :)
Rune FS

지금은 취소 ...하지만 형식 유추를 얻지 못 하기 때문에 실제로 매우 중요 합니다. 내 대답을 참조하십시오.
Jon Skeet

2
Eric의 블로그에서 왜 생성자에 대한 추론이 사양의 일부가 아닌지 알 수 없습니다. 왜 그런지 아십니까?
룬 FS

1

존의 대답은 완벽합니다. 유일한주의 사항은 NHibernate의 HashedSet을 사용하여 결과를 컬렉션으로 변환해야한다는 것입니다. 이를위한 최적의 방법이 있습니까?

ISet<string> bla = new HashedSet<string>((from b in strings select b).ToArray()); 

또는

ISet<string> bla = new HashedSet<string>((from b in strings select b).ToList()); 

아니면 다른 것을 놓치고 있습니까?


편집 : 이것은 내가 끝낸 일입니다.

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
{
    return new HashSet<T>(source);
}

public static HashedSet<T> ToHashedSet<T>(this IEnumerable<T> source)
{
    return new HashedSet<T>(source.ToHashSet());
}

ToList일반적으로보다 더 효율적 ToArray입니다. 그냥 구현해야합니까 ICollection<T>?
Jon Skeet

옳은. 나는 당신의 방법으로 끝내고, 그것을 사용하여 ToHashedSet을 수행합니다 (원래 질문에 대한 편집 참조). 당신의 도움을 주셔서 감사합니다.
Jamie

0

IEnumerable을 HashSet으로 간단히 변환하는 대신 다른 개체의 속성을 HashSet으로 변환하는 것이 편리한 경우가 많습니다. 이것을 다음과 같이 쓸 수 있습니다.

var set = myObject.Select(o => o.Name).ToHashSet();

그러나 선호하는 선택기는 다음과 같습니다.

var set = myObject.ToHashSet(o => o.Name);

그들은 똑같은 일을하고 두 번째는 분명히 짧지 만 관용구가 내 뇌에 더 잘 어울립니다.

다음은 사용자 지정 비교기를 보너스로 지원하는 확장 방법입니다.

public static HashSet<TKey> ToHashSet<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> selector,
    IEqualityComparer<TKey> comparer = null)
{
    return new HashSet<TKey>(source.Select(selector), comparer);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.