कैसे Linq परिणाम को HashSet या HashedSet में कनवर्ट करें


195

मेरे पास एक वर्ग पर एक संपत्ति है जो एक आईएसईटी है। मैं उस संपत्ति में एक 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
जॉगिंग

उत्तर उत्तर-बक्सों में जाते हैं, प्रश्न में ही नहीं। यदि आप अपने स्वयं के प्रश्न का उत्तर देना चाहते हैं, जो एसओ नियमों द्वारा पूरी तरह से ठीक है, तो कृपया उसके लिए एक उत्तर लिखें।
एड्रियन

जवाबों:


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 ऑपरेटरों ( ToDictionary, ToList) के अनुरूप है और भविष्य के विस्तार (जैसे ToSortedSet) के लिए अनुमति देता है । तुम भी उपयोग करने की तुलना निर्दिष्ट एक अधिभार प्रदान करना चाहते हो सकता है।


2
अनाम प्रकार पर अच्छा बिंदु। हालांकि, गुमनाम प्रकार के उपयोगी ओवरराइड करना GetHashCodeऔर Equals? यदि नहीं, तो वे एक HashSet में बहुत अच्छा नहीं होने जा रहे हैं ...
जोएल म्यूएलर

4
@ जोएल: हाँ, वे करते हैं। अन्यथा सभी तरह की चीजें विफल हो जाती। माना जाता है कि वे केवल घटक प्रकारों के लिए डिफ़ॉल्ट समानता तुलनाकर्ताओं का उपयोग करते हैं, लेकिन यह आमतौर पर काफी अच्छा होता है।
जॉन स्कीट

2
@JonSkeet - क्या आप जानते हैं कि अगर कोई कारण है कि यह मानक LINQ ऑपरेटरों में टॉडबर्न और टॉलिस्ट के साथ आपूर्ति नहीं किया गया है? मैंने सुना है कि इस तरह की चीजें गायब होने के अक्सर अच्छे कारण हैं, लापता IEnumerable <T> .ForEach विधि के समान
स्टीफन होल्ट

1
@StephenHolt: मैं प्रतिरोध के साथ सहमत हूं ForEach, लेकिन ToHashSetबहुत अधिक समझ में आता है - मुझे किसी भी कारण के बारे में नहीं पता ToHashSetहै कि सामान्य के अलावा अन्य "उपयोगिता पट्टी को पूरा नहीं करता है" (जिसके साथ मैं असहमत हूं, लेकिन ...)
जॉन स्कीट

1
@ ऐरन: निश्चित नहीं ... संभवत: क्योंकि यह नॉन-लिनक्यू-टू-ऑब्जेक्ट्स प्रदाताओं के लिए सरल नहीं होगा? (मैं कल्पना नहीं कर सकता, यह व्यावहारिक रूप से स्वीकार्य होगा।)
जॉन स्कीट

75

बस अपने IEnumerable को HashSet के लिए कंस्ट्रक्टर में पास करें।

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

2
आप एक प्रक्षेपण के साथ कैसे सामना करने जा रहे हैं जिसके परिणामस्वरूप एक अनाम प्रकार होता है?
जॉन स्कीट

7
@ नहीं, मुझे लगता है कि जब तक अन्यथा की पुष्टि नहीं हो जाती।
एंथनी Pegram

1
स्पष्ट रूप से मैं नहीं हूं। सौभाग्य से, इस विशेष उदाहरण में, मुझे इसकी आवश्यकता नहीं है।
जोएल म्यूलर

@ प्रकार ओपी द्वारा निर्दिष्ट किया गया है (पहली पंक्ति अन्यथा संकलित नहीं करेगी) इसलिए इस मामले में मुझे सहमत होना होगा कि यह अभी के लिए YAGNI है
FS

2
@ रून एफएस: इस मामले में, मुझे संदेह है कि नमूना कोड यथार्थवादी नहीं है। एक प्रकार का पैरामीटर T होना बहुत दुर्लभ है, लेकिन जानते हैं कि प्रत्येक आइटम होने bar.Itemsजा रहा है T। यह देखते हुए कि इस सामान्य उद्देश्य को बनाना कितना आसान है, और LINQ में कितनी बार गुमनाम प्रकार आते हैं, मुझे लगता है कि यह अतिरिक्त कदम उठाने के लायक है।
जॉन स्कीट


19

जैसा कि @ जॉयल ने कहा है, आप अपना प्रवेश योग्य पास कर सकते हैं। यदि आप एक विस्तार विधि करना चाहते हैं, तो आप कर सकते हैं:

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 कोर में 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 ओपी द्वारा निर्दिष्ट प्रकार है :)


यह एक प्रकार का तर्क निर्दिष्ट नहीं करता है।
जॉन स्कीट

लोल को दिन का सबसे कठोर वोट माना जाता है :)। मेरे लिए अब बिल्कुल वही जानकारी है। मुझे धड़कता है कि टाइप इंफ़ेक्शन सिस्टम पहले से ही उस प्रकार का इंफ़ेक्शन क्यों नहीं कर रहा है :)
रुण FS

अब पूर्ववत करें ... लेकिन वास्तव में यह बहुत महत्वपूर्ण है क्योंकि आपको टाइप इंट्रेंस नहीं मिलता है। मेरा जवाब देखिए।
जॉन स्कीट

2
मुझे एरिक के ब्लॉग पर यह देखकर याद नहीं आ रहा है कि कंस्ट्रक्टर्स पर अटकलें कल्पना का हिस्सा क्यों नहीं हैं, क्या आप जानते हैं क्यों?
रुन 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>?
जॉन स्कीट

सही बात। मैंने आपकी विधि के साथ जाना समाप्त कर दिया, फिर ToHashedSet का उपयोग करना (मूल प्रश्न पर संपादित देखें)। आपकी सहायता के लिए धन्यवाद।
जेमी

0

IEnumerable के हैशसेट में साधारण रूपांतरण के बजाय, किसी अन्य ऑब्जेक्ट की संपत्ति को हैशसेट में बदलना अक्सर सुविधाजनक होता है। आप इसे इस प्रकार लिख सकते हैं:

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);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.