Linq sonuçlarını HashSet veya HashedSet'e dönüştürme


196

ISet olan bir sınıfta bir özelliğim var. Bu özelliğe bir linq sorgusu sonuçları almaya çalışıyorum, ama bunu nasıl anlayamıyorum.

Temel olarak, bunun son bölümünü arıyorum:

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

Bunu da yapabilir:

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

Bence HashedSet-parçayı dışarıda bırakmalısın . O zamandan beri kafa karıştırıcı C#ve LINQdenilen bir şey yok HashedSet.
Ağustos'ta Jogge 8

Cevaplar sorunun kendisinde değil cevap kutularında bulunur. SO kurallarına göre gayet iyi olan kendi sorunuzu cevaplamak istiyorsanız, lütfen bunun için bir cevap yazın.
Adriaan

Yanıtlar:


308

Bunu yapan bir şey olduğunu sanmıyorum ... ama bir uzantı yöntemi yazmak gerçekten kolay:

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

Burada gerçekten bir uzantı yöntemi (veya en azından bir formun genel bir yöntemi) istediğinizi unutmayın, çünkü türünü Taçıkça ifade edemeyebilirsiniz :

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

Bunu yapıcıya açık bir çağrı ile yapamazsınız HashSet<T>. Bizim için genel yöntemler için tür çıkarımlarına güveniyoruz.

Şimdi olabilir Adını tercih ToSetve getiri ISet<T>- ama ile sopa ToHashSetve beton türü. Bu, standart LINQ operatörleriyle ( ToDictionary, ToList) tutarlıdır ve gelecekteki genişlemeye (ör. ToSortedSet) Olanak tanır . Kullanılacak karşılaştırmayı belirten bir aşırı yük de sağlamak isteyebilirsiniz.


2
Anonim türlerde iyi bir nokta. Ancak, anonim türlerin GetHashCodeve Equals? Değilse, bir HashSet'te çok iyi olmayacaklar ...
Joel Mueller

4
@Joel: Evet, var. Aksi takdirde her türlü şey başarısız olur. Kuşkusuz, bileşen türleri için sadece varsayılan eşitlik karşılaştırıcılarını kullanırlar, ancak bu genellikle yeterince iyidir.
Jon Skeet

2
@JonSkeet - Bunun ToDictionary ve ToList ile birlikte standart LINQ operatörlerinde bulunmamasının bir nedeni olup olmadığını biliyor musunuz? IEnumerable <T> gibi bu tür şeylerin atlanmasının genellikle iyi nedenleri olduğunu duydum .ForEach yöntemi
Stephen Holt

1
@StephenHolt: Direnişe katılıyorum ForEach, ama ToHashSetçok daha mantıklı - ToHashSetNormal "kullanışlılık çubuğunu karşılamıyor" dan başka bir şey yapmamak için hiçbir neden bilmiyorum (ki buna katılmıyorum, ama ...)
Jon Skeet

1
@Aaron: Emin değilim ... muhtemelen LINQ'dan Nesneye olmayan sağlayıcılar için bu kadar basit olmayacağı için mi? (Kuşkusuz bunun mümkün olmadığını hayal edemiyorum.)
Jon Skeet

75

IEnumerable'ınızı HashSet yapıcısına iletmeniz yeterlidir.

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

2
Anonim tipte bir projeksiyonla nasıl başa çıkacaksınız?
Jon Skeet

7
@Jon, aksi onaylanana kadar bunun YAGNI olduğunu varsayarım.
Anthony Pegram

1
Açıkçası değilim. Neyse ki, bu özel örnekte, buna ihtiyacım yok.
Joel Mueller

@Jon türü OP tarafından belirtilir (ilk satır başka türlü derleyici olmaz) bu durumda şimdilik YAGNI olduğunu kabul etmeliyim
Rune FS

2
@Rune FS: Bu durumda, örnek kodun gerçekçi olmadığından şüpheleniyorum. Bir T tipi parametresine sahip olmak oldukça nadirdir, ancak her öğesinin bar.Itemsolacağını bilin T. Bu genel amacı yapmanın ne kadar kolay olduğu ve LINQ'da anonim türlerin ne sıklıkta ortaya çıktığı göz önüne alındığında, ekstra adım atmaya değer olduğunu düşünüyorum.
Jon Skeet


19

@Joel'in belirttiği gibi, numaralandırılabilir öğenizi iletebilirsiniz. Bir uzantı yöntemi yapmak istiyorsanız, şunları yapabilirsiniz:

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

3

Kümeye salt okunur erişime ihtiyacınız varsa ve kaynak yönteminizin bir parametresiyse,

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

Nedeni, kullanıcıların ISetzaten yönteminizi çağırabilir, böylece kopyayı oluşturmanız gerekmez.


3

.NET çerçevesinde ve .NET çekirdeğin bir uzantısı yöntemi yapı, bir dönüştürme için mevcuttur 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);

Henüz .NET standart kitaplıklarında (yazma sırasında) kullanamadığım anlaşılıyor. Sonra bu uzantı yöntemini kullanın:

    [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

Bu oldukça basit :)

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

ve evet T OP tarafından belirtilen türdür :)


Bu bir tür argümanı belirtmez.
Jon Skeet

Lol, günün en sert oyu olmalı :). Bana göre şimdi aynı bilgi var. Neden tür çıkarım sistemi zaten zaten bu tür çıkarım değil beni yener :)
Rune FS

Şimdi geri alındı ​​... ama aslında tam anlamıyla önemli çünkü tip çıkarımınız yok. Cevabımı gör.
Jon Skeet

2
Eric'in blogunda yapıcıları çıkarmanın neden spesifikasyonların bir parçası olmadığını gördüğümü hatırlayamıyorum, neden biliyor musunuz?
Rune FS

1

Jon'un cevabı mükemmel. Tek uyarı, NHibernate'in HashedSet'ini kullanarak sonuçları bir koleksiyona dönüştürmem gerektiğidir. Bunu yapmanın en uygun yolu var mı?

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

veya

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

Yoksa başka bir şey mi kaçırıyorum?


Düzenleme: Bu ne kadar sona erdi:

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

ToListgenellikle daha verimlidir ToArray. Sadece uygulanması gerekiyor ICollection<T>mu?
Jon Skeet

Doğru. Metodunuzla devam ettim, sonra bunu ToHashedSet yapmak için kullandım (orijinal sorudaki düzenlemeye bakın). Yardımınız için teşekkürler.
Jamie

0

IEnumerable'ın bir HashSet'e basitçe dönüştürülmesinden ziyade, başka bir nesnenin bir özelliğini HashSet'e dönüştürmek genellikle uygundur. Bunu şöyle yazabilirsiniz:

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

ama benim tercihim seçicileri kullanmak olacaktır:

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

Aynı şeyi yaparlar ve ikincisi açıkça daha kısadır, ancak deyim beynime daha iyi uyuyor (ToDictionary gibi olduğunu düşünüyorum).

Bonus olarak özel karşılaştırıcılar desteğiyle kullanılacak uzantı yöntemi.

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);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.