एक IEnumerable में एक तत्व का सूचकांक कैसे प्राप्त करें?


144

मैंने यह लिखा:

public static class EnumerableExtensions
{
    public static int IndexOf<T>(this IEnumerable<T> obj, T value)
    {
        return obj
            .Select((a, i) => (a.Equals(value)) ? i : -1)
            .Max();
    }

    public static int IndexOf<T>(this IEnumerable<T> obj, T value
           , IEqualityComparer<T> comparer)
    {
        return obj
            .Select((a, i) => (comparer.Equals(a, value)) ? i : -1)
            .Max();
    }
}

लेकिन मुझे नहीं पता कि क्या यह पहले से मौजूद है, है ना?


4
एक के साथ समस्या यह Maxदृष्टिकोण है कि एक है: यह देख रहता है, और ख: यह रिटर्न पिछले जब डुप्लिकेट (लोगों को आमतौर पर पहले सूचकांक उम्मीद) देखते हैं सूचकांक
मार्क Gravell

1
geekswithblogs.net 4 समाधानों और उनके प्रदर्शन की तुलना करता है। यह ToList()/FindIndex()ट्रिक सबसे अच्छा प्रदर्शन करती है
निक्सन

जवाबों:


51

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


8
वर्तमान में मैं इस धागे के पार आया हूं क्योंकि मैं IEnumerable <> प्रकार के लिए एक सामान्य IList <> रैपर लागू कर रहा हूं ताकि मेरे IEnumerable <> तृतीय पक्ष घटकों के साथ वस्तुओं का उपयोग किया जा सके जो केवल IList प्रकार के डेटा स्रोत का समर्थन करता है। मैं मानता हूं कि एक IEnumerable वस्तु के भीतर एक तत्व का एक सूचकांक प्राप्त करने की कोशिश करना संभवतया ज्यादातर मामलों में कुछ गलत होने का संकेत है, ऐसे समय होते हैं जब ऐसा सूचकांक एक बार एक बड़े संग्रह को पुनः प्राप्त करने के लिए याद रखता है केवल सूचकांक खोजने के लिए। एक तत्व जब आप पहले से ही एक IEnumerable है।
17

215
-1 कारण: वैध कारण हैं कि आप किसी इंडेक्स को बाहर क्यों लाना चाहते हैं IEnumerable<>। मैं पूरी खरीद नहीं "आप इस" हठधर्मिता करना होगा।
जॉन अलेक्सिउ

78
@ Ja72 से सहमत; यदि आप के साथ अनुक्रमणिका के साथ व्यवहार नहीं किया जाना चाहिए IEnumerableतो Enumerable.ElementAtमौजूद नहीं होगा। IndexOfबस उलटा है - इसके खिलाफ कोई भी तर्क समान रूप से लागू होना चाहिए ElementAt
किर्क वोल्फ

7
क्लीरी, C # IIndexableEnumerable की अवधारणा को याद करता है। वह C ++ STL शब्दावली में "यादृच्छिक अभिगम योग्य" अवधारणा के समतुल्य होगा।
v.oddou

14
चयन (जैसे (x, i) =>) जैसे अधिभार के साथ विस्तार से लगता है कि ये सूचकांक मौजूद होना चाहिए
माइकल

126

मैं ज्ञान पर सवाल उठाऊंगा, लेकिन शायद:

source.TakeWhile(x => x != value).Count();

( जरूरत पड़ने EqualityComparer<T>.Defaultपर अनुकरण करने के लिए उपयोग करना !=) - लेकिन आपको रिटर्न -1 देखने की आवश्यकता है यदि नहीं मिला है ... तो शायद यह लंबा रास्ता तय करे

public static int IndexOf<T>(this IEnumerable<T> source, T value)
{
    int index = 0;
    var comparer = EqualityComparer<T>.Default; // or pass in as a parameter
    foreach (T item in source)
    {
        if (comparer.Equals(item, value)) return index;
        index++;
    }
    return -1;
}

8
"ज्ञान पर सवाल उठाने" के लिए +1। 10 में से 9 बार यह पहली जगह में शायद एक बुरा विचार है।
जोएल कोएहॉर्न

स्पष्ट लूप समाधान भी सेलेक्ट () मैक्स () समाधान की तुलना में 2x सबसे तेज (सबसे खराब स्थिति में) चलाता है।
स्टीव गाइडी

1
आप सिर्फ तावले के बिना लंबोदर द्वारा तत्वों की गणना कर सकते हैं - यह एक लूप बचाता है: source.Count (x => x! = =);
कामारेई

10
@ कामारे - नहीं, वह कुछ अलग करता है। टेकहाइल रुक जाता है जब यह एक विफलता हो जाती है; गणना (विधेय) जो मेल खाते हैं उन्हें लौटाता है। यानी अगर पहली मिस थी और बाकी सब कुछ सच था, टेकव्हील (पूर्व) .काउंट () रिपोर्ट करेगा 0; गणना (पूर्व) n-1 की रिपोर्ट करेगी।
मार्क Gravell

1
TakeWhileचतुर है! ध्यान रखें कि यह रिटर्न Countअगर तत्व मौजूद नहीं है जो मानक व्यवहार से विचलन है।
नवफाल

27

मैं इसे इस तरह लागू करूंगा:

public static class EnumerableExtensions
{
    public static int IndexOf<T>(this IEnumerable<T> obj, T value)
    {
        return obj.IndexOf(value, null);
    }

    public static int IndexOf<T>(this IEnumerable<T> obj, T value, IEqualityComparer<T> comparer)
    {
        comparer = comparer ?? EqualityComparer<T>.Default;
        var found = obj
            .Select((a, i) => new { a, i })
            .FirstOrDefault(x => comparer.Equals(x.a, value));
        return found == null ? -1 : found.i;
    }
}

1
यह वास्तव में बहुत प्यारा है, +1! इसमें अतिरिक्त ऑब्जेक्ट शामिल हैं, लेकिन वे अपेक्षाकृत सस्ते (GEN0) होने चाहिए , इसलिए बहुत बड़ी समस्या नहीं है। ==काम की जरूरत हो सकती?
मार्क Gravell

1
सत्य LINQ शैली में IEqualityComparer अधिभार जोड़ा गया। ;)
dahlbyk

1
मुझे लगता है कि आपके कहने का मतलब है ... तुलनित्र (एक्वा, मूल्य) =)
मार्क

चूंकि सेलेक्ट एक्सप्रेशन संयुक्त परिणाम को लौटा रहा है, जो तब संसाधित किया गया है, मैं स्पष्ट रूप से KeyValuePair मान प्रकार का उपयोग करने की कल्पना करता हूं, जिससे आप किसी भी प्रकार के ढेर आवंटन से बच सकते हैं, इसलिए जब तक .NET निहितार्थ स्टैक पर मान प्रकार आवंटित करता है और कोई भी राज्य मशीन, जो LINQ जनरेट कर सकती है, Select'd result के लिए एक फ़ील्ड का उपयोग करती है जिसे नंगे ऑब्जेक्ट के रूप में घोषित नहीं किया गया है (इस प्रकार KVP परिणाम बॉक्सिंग प्राप्त करने का कारण बनता है)। बेशक, आपको पाया गया == अशक्त स्थिति को फिर से काम करना होगा (क्योंकि पाया गया अब KVP मान होगा)। शायद DefaultIfEmpty () या KVP<T, int?>(
अशक्त

1
अच्छा कार्यान्वयन, हालांकि एक बात जो मैं जोड़ना चाहूंगा, वह यह देखने के लिए है कि क्या यह लागू होता है IList<T>या नहीं, यह देखने के लिए एक जाँच है , अगर यह एक विशिष्ट प्रकार के अनुकूलन के मामले में सिर्फ अपनी IndexOf विधि के लिए सुरक्षित है।
जोश

16

जिस तरह से मैं वर्तमान में यह कर रहा हूं वह पहले से सुझाए गए से थोड़ा कम है और जहां तक ​​मैं बता सकता हूं वांछित परिणाम देता है:

 var index = haystack.ToList().IndexOf(needle);

यह थोड़ा सांवला है, लेकिन यह काम करता है और काफी संक्षिप्त है।


6
हालांकि यह छोटे संग्रह के लिए काम करेगा, मान लीजिए कि आपके पास "हिस्टैक" में एक लाख आइटम हैं। उस पर एक ToList () कर सभी एक लाख तत्वों के माध्यम से पुनरावृत्ति करेगा और उन्हें एक सूची में जोड़ देगा। फिर यह सूची के माध्यम से खोज करेगा कि मिलान तत्व का सूचकांक क्या है। यह अत्यंत अक्षम होने के साथ-साथ सूची को बहुत बड़ा होने पर अपवाद को फेंकने की संभावना भी होगी।
17

3
@esteuart निश्चित रूप से - आपको एक दृष्टिकोण चुनने की आवश्यकता है जो आपके उपयोग के मामले के अनुरूप हो। मुझे संदेह है कि एक आकार सभी समाधान फिट बैठता है, जो संभवतः इसलिए है कि कोर पुस्तकालयों में कार्यान्वयन नहीं है।
मार्क वॉट्स

8

स्थिति को पकड़ने का सबसे अच्छा तरीका है FindIndexयह फ़ंक्शन केवल के लिए उपलब्ध हैList<>

उदाहरण

int id = listMyObject.FindIndex(x => x.Id == 15); 

यदि आपके पास एन्यूमरेटर या सरणी है तो इस तरह से उपयोग करें

int id = myEnumerator.ToList().FindIndex(x => x.Id == 15); 

या

 int id = myArray.ToList().FindIndex(x => x.Id == 15); 

7

मुझे लगता है कि इस तरह लागू करने के लिए सबसे अच्छा विकल्प है:

public static int IndexOf<T>(this IEnumerable<T> enumerable, T element, IEqualityComparer<T> comparer = null)
{
    int i = 0;
    comparer = comparer ?? EqualityComparer<T>.Default;
    foreach (var currentElement in enumerable)
    {
        if (comparer.Equals(currentElement, element))
        {
            return i;
        }

        i++;
    }

    return -1;
}

यह अनाम ऑब्जेक्ट भी नहीं बनाएगा


5

खेल में थोड़ी देर, मुझे पता है ... लेकिन यह वही है जो मैंने हाल ही में किया था। यह आपकी तुलना में थोड़ा अलग है, लेकिन प्रोग्रामर को यह निर्धारित करने की अनुमति देता है कि समानता ऑपरेशन (विधेय) की आवश्यकता क्या है। जो मुझे विभिन्न प्रकारों के साथ काम करते समय बहुत उपयोगी लगता है, क्योंकि मेरे पास ऑब्जेक्ट प्रकार की परवाह किए बिना इसे करने का एक सामान्य तरीका है और<T> समानता ऑपरेटर में निर्मित ।

इसकी एक बहुत छोटी स्मृति पदचिह्न भी है, और बहुत, बहुत तेज / कुशल है ... यदि आप इस बारे में परवाह करते हैं।

इससे भी बदतर, आप इसे अपने एक्सटेंशन की सूची में जोड़ देंगे।

वैसे भी ... यहाँ यह है।

 public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
 {
     int retval = -1;
     var enumerator = source.GetEnumerator();

     while (enumerator.MoveNext())
     {
         retval += 1;
         if (predicate(enumerator.Current))
         {
             IDisposable disposable = enumerator as System.IDisposable;
             if (disposable != null) disposable.Dispose();
             return retval;
         }
     }
     IDisposable disposable = enumerator as System.IDisposable;
     if (disposable != null) disposable.Dispose();
     return -1;
 }

उम्मीद है कि यह किसी की मदद करता है।


1
शायद मुझे कुछ याद आ रहा है, लेकिन सिर्फ GetEnumeratorऔर MoveNextसिर्फ क्यों foreach?
जोश गलाघेर 23

1
संक्षिप्त जवाब? दक्षता। लंबा उत्तर: msdn.microsoft.com/en-us/library/9yb8xew9.aspx
MaxOvrdrv

2
आईएल को देखते हुए यह प्रतीत होता है कि प्रदर्शन अंतर यह है कि अगर यह लागू होता है तो एन्यूमरेटर पर foreachकॉल करेगा । (देखें stackoverflow.com/questions/4982396/… ) जैसा कि इस उत्तर में कोड को पता नहीं है कि कॉलिंग का परिणाम है या डिस्पोजेबल नहीं है, इसे भी ऐसा ही करना चाहिए। उस बिंदु पर मैं अभी भी स्पष्ट नहीं हूं कि एक पूर्ण लाभ है, हालांकि कुछ अतिरिक्त आईएल था, जिसका उद्देश्य मुझ पर छलांग नहीं लगाता था! DisposeIDisposableGetEnumerator
जोश गलाघेर

@JoshGallagher मैंने कुछ समय पहले foreach और for (i) के बीच के पूर्ण लाभों के बारे में कुछ शोध किया था, और (i) के लिए उपयोग करने का मुख्य लाभ यह था कि इसे दोबारा बनाने या पास करने के बजाय ऑब्जेक्ट को इन-प्लेस कर देता है बैक बायल। मैं यही बात MoveNext बनाम foreach पर लागू होता है, लेकिन मुझे उस पर यकीन नहीं है। शायद वे दोनों ByVal का उपयोग करते हैं ...
MaxOvrdrv

2
इस ब्लॉग को पढ़ना ( blogs.msdn.com/b/ericlippert/archive/2010/09/30/… ) यह हो सकता है कि जिस " इटेरेटर लूप" का वह जिक्र कर रहा है वह एक foreachलूप है, जिसमें किसी विशेष मामले के लिए Tमूल्य प्रकार होने के नाते यह लूप का उपयोग करके बॉक्स / अनबॉक्स ऑपरेशन को सहेज सकता है। हालाँकि, यह आईएल द्वारा वहन नहीं किया गया है, मुझे आपके उत्तर के एक संस्करण से मिला है foreach। मुझे अब भी लगता है कि इटरेटर का सशर्त निपटान महत्वपूर्ण है, हालांकि। क्या आप इसमें शामिल होने के उत्तर को संशोधित कर सकते हैं?
जोश गैलाघर

5

कुछ साल बाद, लेकिन यह Linq का उपयोग करता है, रिटर्न -1 मिलता है, यदि नहीं मिला, तो अतिरिक्त ऑब्जेक्ट नहीं बनाता है, और शॉर्ट सर्किट होना चाहिए जब पाया जाता है [जैसा कि पूरे IEnumerable पर चलने का विरोध किया गया]:

public static int IndexOf<T>(this IEnumerable<T> list, T item)
{
    return list.Select((x, index) => EqualityComparer<T>.Default.Equals(item, x)
                                     ? index
                                     : -1)
               .FirstOr(x => x != -1, -1);
}

जहां 'FirstOr' है:

public static T FirstOr<T>(this IEnumerable<T> source, T alternate)
{
    return source.DefaultIfEmpty(alternate)
                 .First();
}

public static T FirstOr<T>(this IEnumerable<T> source, Func<T, bool> predicate, T alternate)
{
    return source.Where(predicate)
                 .FirstOr(alternate);
}

इसे करने का एक और तरीका हो सकता है: सार्वजनिक स्थिर int IndexOf <T> (यह IEnumerable <T> सूची, T आइटम) {int e = list.Select ((x, index) => EqualityComparer <T> .Default.Equals ()। आइटम, x); x + 1: -1) .FirstOrDefault (x => x> 0); वापसी (ई == 0)? -1: ई - 1); }
अनु थॉमस चांडी

"अतिरिक्त ऑब्जेक्ट नहीं बनाता है"। Linq वास्तव में पृष्ठभूमि में वस्तुओं का निर्माण करेगा, इसलिए यह पूरी तरह से सही नहीं है। उदाहरण के लिए दोनों source.Whereऔर प्रत्येक source.DefaultIfEmptyएक बनाएँ IEnumerable
मार्टिन ओधलीस

1

जवाब के लिए एक खोज में आज इस पर ठोकर खाई और मुझे लगा कि मैं अपने संस्करण को सूची में जोड़ दूंगा (कोई सज़ा नहीं)। यह c # 6.0 के अशक्त सशर्त संचालक को दर्शाता है

IEnumerable<Item> collection = GetTheCollection();

var index = collection
.Select((item,idx) => new { Item = item, Index = idx })
//or .FirstOrDefault(_ =>  _.Item.Prop == something)
.FirstOrDefault(_ => _.Item == itemToFind)?.Index ?? -1;

मैंने कुछ 'पुराने घोड़ों की दौड़' (परीक्षण) और बड़े संग्रह (~ 100,000) के लिए किया है, सबसे खराब स्थिति यह है कि जो आइटम आप चाहते हैं वह अंत में है, यह करने की तुलना में तेजी से 2x हैToList().FindIndex() । यदि आप जो आइटम चाहते हैं वह इसके ~ 4x के बीच में है तेज।

छोटे संग्रह (~ 10,000) के लिए यह केवल मामूली रूप से तेज प्रतीत होता है

Heres मैंने इसे कैसे जांचा https://gist.github.com/insulind/16310945247fcf13ba186a45734f254e


1

@Marc Gravell के उत्तर का उपयोग करते हुए, मुझे निम्नलिखित विधि का उपयोग करने का एक तरीका मिला:

source.TakeWhile(x => x != value).Count();

जब आइटम नहीं मिल सकता है तो -1 पाने के लिए:

internal static class Utils
{

    public static int IndexOf<T>(this IEnumerable<T> enumerable, T item) => enumerable.IndexOf(item, EqualityComparer<T>.Default);

    public static int IndexOf<T>(this IEnumerable<T> enumerable, T item, EqualityComparer<T> comparer)
    {
        int index = enumerable.TakeWhile(x => comparer.Equals(x, item)).Count();
        return index == enumerable.Count() ? -1 : index;
    }
}

मुझे लगता है कि यह तरीका सबसे तेज और सरल दोनों हो सकता है। हालाँकि, मैंने अभी तक प्रदर्शन का परीक्षण नहीं किया है।


0

तथ्य के बाद सूचकांक को खोजने के लिए एक विकल्प एनुमेरबल को लपेटने के लिए है, कुछ हद तक Linq GroupBy () पद्धति का उपयोग करने के समान है।

public static class IndexedEnumerable
{
    public static IndexedEnumerable<T> ToIndexed<T>(this IEnumerable<T> items)
    {
        return IndexedEnumerable<T>.Create(items);
    }
}

public class IndexedEnumerable<T> : IEnumerable<IndexedEnumerable<T>.IndexedItem>
{
    private readonly IEnumerable<IndexedItem> _items;

    public IndexedEnumerable(IEnumerable<IndexedItem> items)
    {
        _items = items;
    }

    public class IndexedItem
    {
        public IndexedItem(int index, T value)
        {
            Index = index;
            Value = value;
        }

        public T Value { get; private set; }
        public int Index { get; private set; }
    }

    public static IndexedEnumerable<T> Create(IEnumerable<T> items)
    {
        return new IndexedEnumerable<T>(items.Select((item, index) => new IndexedItem(index, item)));
    }

    public IEnumerator<IndexedItem> GetEnumerator()
    {
        return _items.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

जिसका उपयोग मामला देता है:

var items = new[] {1, 2, 3};
var indexedItems = items.ToIndexed();
foreach (var item in indexedItems)
{
    Console.WriteLine("items[{0}] = {1}", item.Index, item.Value);
}

महान आधार रेखा। यह सदस्यों को जोड़ने के लिए सहायक है, IsOdd, IsFirst और IsLast के रूप में अच्छी तरह से।
JJS

0

यह वास्तव में विस्तार के साथ शांत हो सकता है (उदाहरण के लिए कार्य करना), उदाहरण के लिए:

collection.SelectWithIndex(); 
// vs. 
collection.Select((item, index) => item);

जो इस Indexसंपत्ति के माध्यम से पहुंच को स्वचालित रूप से अनुक्रमणिका को असाइन करेगा ।

इंटरफेस:

public interface IIndexable
{
    int Index { get; set; }
}

कस्टम एक्सटेंशन (शायद EF और DbContext के साथ काम करने के लिए सबसे उपयोगी):

public static class EnumerableXtensions
{
    public static IEnumerable<TModel> SelectWithIndex<TModel>(
        this IEnumerable<TModel> collection) where TModel : class, IIndexable
    {
        return collection.Select((item, index) =>
        {
            item.Index = index;
            return item;
        });
    }
}

public class SomeModelDTO : IIndexable
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }

    public int Index { get; set; }
}

// In a method
var items = from a in db.SomeTable
            where a.Id == someValue
            select new SomeModelDTO
            {
                Id = a.Id,
                Name = a.Name,
                Price = a.Price
            };

return items.SelectWithIndex()
            .OrderBy(m => m.Name)
            .Skip(pageStart)
            .Take(pageSize)
            .ToList();
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.