linqの結果をHashSetまたはHashedSetに変換する方法


195

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-part は省略すべきだと思います。それはちょうどので、混乱しているC#LINQ呼ばれるものを持っていませんHashedSet
Jogge

回答は質問自体ではなく、回答ボックスに入れられます。あなた自身の質問に答えたい場合、それはSOルールによって完全にうまくあります、そのための答えを書いてください。
Adriaan

回答:


307

これを行うものは何も組み込まれていないと思いますが、拡張メソッドを書くのは本当に簡単です。

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>コンストラクタを明示的に呼び出すことでそれを行うことはできません。ジェネリックメソッドが型推論を行うのは、型推論に依存しています。

今、あなたは可能性があり、それ命名することを選択したToSetとリターンをISet<T>-しかし、私はに固執したいToHashSetとコンクリートタイプ。これは、標準のLINQ演算子(ToDictionaryToList)と一貫性があり、将来の拡張(例ToSortedSet)を可能にします。使用する比較を指定するオーバーロードを提供することもできます。


2
匿名型の良い点。ただし、匿名型には、GetHashCodeおよびの便利なオーバーライドがありEqualsますか?そうでない場合、HashSetはそれほど良くありません...
Joel Mueller

4
@ジョエル:はい、そうです。そうでなければ、あらゆる種類のものが失敗します。確かに、コンポーネントタイプのデフォルトの等値比較子のみを使用していますが、通常はそれで十分です。
Jon Skeet

2
@JonSkeet-これがToDictionaryやToListと共に標準のLINQ演算子で提供されない理由があるかどうか知っていますか?私はそのような事が省略されている理由は、多くの場合、十分な理由があることを聞いた行方不明のIEnumerable <T> .ForEach方法に類似
スティーブン・ホルト

1
@StephenHolt:私はへの耐性に同意するForEachが、ToHashSet多くの理にかなって-私はしない何らかの理由を知らないToHashSet私は反対するだろう(「有用性バーを満たしていない」通常以外の、しかし、 ...)
Jon Skeet 2013

1
@Aaron:わからない...多分それは非LINQ-to-Objectsプロバイダーにとってそれほど単純ではないためでしょうか?(私はそれが現実的に実行不可能だとは想像できません。)
ジョン・スキート、

75

IEnumerableをHashSetのコンストラクターに渡すだけです。

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

2
匿名型になる投影にどう対処しますか?
Jon Skeet、2010

7
@ジョン、私は別の方法で確認されるまではYAGNIだと思います。
Anthony Pegram 2010

1
明らかに私は違います。幸いなことに、この特定の例では必要ありません。
Joel Mueller

@JonタイプはOPによって指定されます(最初の行はそれ以外の場合はコンパイラーではありません)。この場合、今のところYAGNIであることに同意する必要があります
Rune FS

2
@Rune FS:この場合、サンプルコードは現実的ではないと思われます。型パラメーターTを持つことは非常にまれですが、の各項目がになることを知っていbar.ItemsますT。この汎用的な目的の作成がどれほど簡単であり、匿名型がLINQで頻繁に出現するかを考えると、追加の手順を実行する価値があると思います。
Jon Skeet


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フレームワークおよび変換するための.NETコアに拡張メソッドのビルドがあるIEnumerableHashSethttps://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、2010

その日の最悪の投票でなければならない笑:) 私には今とまったく同じ情報があります。型推論システムがその型をまだ推論していない理由を私に打ち明けます:)
Rune FS

元に戻す...しかし、型推論を取得しないため、実際にはかなり重要です。私の答えを見てください。
Jon Skeet、2010

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>ですか?
ジョンスキート2010

正しい。私はあなたの方法に行き、それを使ってToHashedSetを行いました(元の質問の編集を参照)。ご協力いただきありがとうございます。
ジェイミー

0

IEnumerableをHashSetに単純に変換するのではなく、別のオブジェクトのプロパティをHashSetに変換すると便利な場合があります。これは次のように書くことができます:

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

しかし、私の好みはセレクターを使用することです:

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

彼らは同じことをし、2番目は明らかに短いですが、私はイディオムが私の脳によりよく合うと思います(私はそれをToDictionaryのように考える)。

使用する拡張メソッドは次のとおりです。ボーナスとしてカスタムコンパレータをサポートしています。

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.