सूची <T> में दो आइटम स्वैप करें


82

वहाँ एक LINQ एक अंदर दो वस्तुओं की स्थिति स्वैप तरीका है list<T>?


57
इससे कोई फर्क क्यों पड़ता है कि वह ऐसा क्यों करना चाहता है। जो लोग "स्वैप सूची आइटम c #" के लिए googling कर रहे हैं वे इस विशिष्ट प्रश्न का सीधा उत्तर चाहते हैं।
डैनियल मैकियास

10
@DanielMacias यह सच है। ये जवाब जो 'जैसा है, लेकिन आप ऐसा क्यों कर रहे हैं?' बहुत गुस्सा आ रहे हैं। मुझे लगता है कि लोगों को बहस करने से पहले कम से कम व्यवहार्य जवाब देना चाहिए।
15:27 बजे ज्यूलगॉन

ऐसा करने के लिए आप LINQ का उपयोग क्यों करना चाहते हैं? अगर LINQ विशिष्ट है तो LINQ
ina

जवाबों:


119

मार्क का उत्तर C # से देखें: स्वैप विधि का अच्छा / सर्वोत्तम कार्यान्वयन

public static void Swap<T>(IList<T> list, int indexA, int indexB)
{
    T tmp = list[indexA];
    list[indexA] = list[indexB];
    list[indexB] = tmp;
}

जो कि linq-i-fied की तरह हो सकता है

public static IList<T> Swap<T>(this IList<T> list, int indexA, int indexB)
{
    T tmp = list[indexA];
    list[indexA] = list[indexB];
    list[indexB] = tmp;
    return list;
}

var lst = new List<int>() { 8, 3, 2, 4 };
lst = lst.Swap(1, 2);

10
इस विस्तार पद्धति को सूची वापस करने की आवश्यकता क्यों है? आप सूची को जगह-जगह संशोधित कर रहे हैं।
वार्गोनियन

13
तब आप विधियों की श्रृंखला बना सकते हैं।
Jan Jongboom

यदि आप Unity3D में इसका उपयोग करने के लिए परेशान कर रहे हैं, तो विस्तार विधि को सार्वजनिक करना न भूलें! (अगर आप ऐसा नहीं करते हैं तो एकता इसे नहीं पा सकती है)
col000r

अच्छा और आसान। धन्यवाद
fnc12

5
चेतावनी: "LINQified" संस्करण अभी भी मूल सूची को बदल देता है।
फिलिप माइकल्स्की

32

हो सकता है कि कोई ऐसा करने के लिए एक चतुर तरीका सोचेगा, लेकिन आपको ऐसा नहीं करना चाहिए। एक सूची में दो आइटमों की अदला-बदली स्वाभाविक रूप से साइड-इफेक्ट लादेन है लेकिन LINQ ऑपरेशंस साइड-इफ़ेक्ट फ्री होना चाहिए। इस प्रकार, बस एक साधारण एक्सटेंशन विधि का उपयोग करें:

static class IListExtensions {
    public static void Swap<T>(
        this IList<T> list,
        int firstIndex,
        int secondIndex
    ) {
        Contract.Requires(list != null);
        Contract.Requires(firstIndex >= 0 && firstIndex < list.Count);
        Contract.Requires(secondIndex >= 0 && secondIndex < list.Count);
        if (firstIndex == secondIndex) {
            return;
        }
        T temp = list[firstIndex];
        list[firstIndex] = list[secondIndex];
        list[secondIndex] = temp;
    }
}

दुष्प्रभाव? क्या आप विस्तार से समझा सकते हैं?
टोनी द लॉयन

12
साइड इफेक्ट्स से उनका मतलब है कि वे सूची में बदलाव करते हैं और संभवतः सूची में आइटम - बस क्वेरी डेटा के विरोध में जो कुछ भी संशोधित नहीं करेगा
15

"T अस्थायी = सूची [firstIndex];" सूची में ऑब्जेक्ट की गहरी प्रतिलिपि बनाएँ?
पॉल मैकार्थी

1
@PaMMcCarthy नहीं, यह मूल ऑब्जेक्ट के लिए एक नया पॉइंटर बनाता है, कोई भी नकल नहीं करता है।
नेटमैज

12

List<T>एक Reverse()विधि है, हालांकि यह केवल दो (या अधिक) लगातार वस्तुओं के आदेश को उलट देती है ।

your_list.Reverse(index, 2);

जहां दूसरा पैरामीटर 2इंगित करता है कि हम 2 आइटम के क्रम को उलट रहे हैं, दिए गए आइटम के साथ शुरू होता है index

स्रोत: https://msdn.microsoft.com/en-us/library/hf2ay11y(v=vs.110).aspx


1
हालाँकि Google ने मुझे यहाँ लाया, यह वही है जिसकी मुझे तलाश थी।
जूनियर

1
.Reverse () फ़ंक्शन Linq नहीं है और इसे Swap नहीं कहा जाता है, लेकिन ऐसा लगता है कि प्रश्न का आशय एक "निर्मित" फ़ंक्शन को खोजने के लिए है जो एक स्वैप करता है। यह एकमात्र उत्तर है जो प्रदान करता है।
अमेरिका

लेकिन केवल आसन्न तत्वों को स्वैप करने के लिए उपयोगी है, इसलिए वास्तव में जवाब नहीं है।
नेटमैसेज

10

कोई मौजूदा स्वैप-विधि नहीं है, इसलिए आपको एक स्वयं बनाना होगा। बेशक आप इसे linqify कर सकते हैं, लेकिन यह एक (अलिखित) के साथ किया जाना चाहिए: नियमों को ध्यान में रखते हुए: LINQ- ऑपरेशन इनपुट मापदंडों को नहीं बदलता है!

अन्य "लाइनक्वाइज़" उत्तरों में, इनपुट (इनपुट) सूची को संशोधित और लौटाया जाता है, लेकिन यह कार्रवाई उस नियम को ब्रेक देती है। यदि आप विचित्र वस्तुओं के साथ एक सूची रखते हैं, तो अजीब होगा, एक LINQ "ऑर्डरबाय" -प्रकरण करें और यह पता लगाने के लिए कि इनपुट सूची भी क्रमबद्ध है (परिणाम की तरह)। यह होने की अनुमति नहीं है!

इसे कैसे किया जा सकता है?

मेरा पहला विचार यह था कि संग्रह को समाप्त करने के बाद इसे पुन: व्यवस्थित करना था। लेकिन यह एक गंदा उपाय है, इसलिए इसका उपयोग न करें:

static public IEnumerable<T> Swap1<T>(this IList<T> source, int index1, int index2)
{
    // Parameter checking is skipped in this example.

    // Swap the items.
    T temp = source[index1];
    source[index1] = source[index2];
    source[index2] = temp;

    // Return the items in the new order.
    foreach (T item in source)
        yield return item;

    // Restore the collection.
    source[index2] = source[index1];
    source[index1] = temp;
}

यह समाधान गंदा है क्योंकि यह इनपुट सूची को संशोधित करता है , भले ही यह इसे मूल स्थिति में पुनर्स्थापित करता हो। इससे कई समस्याएं हो सकती हैं:

  1. सूची को आसानी से पढ़ा जा सकता है जो एक अपवाद को फेंक देगा।
  2. यदि सूची को कई थ्रेड्स द्वारा साझा किया जाता है, तो इस फ़ंक्शन की अवधि के दौरान अन्य थ्रेड्स के लिए सूची बदल जाएगी।
  3. यदि पुनरावृत्ति के दौरान कोई अपवाद होता है, तो सूची को पुनर्स्थापित नहीं किया जाएगा। (यह स्वैप-फ़ंक्शन के अंदर एक कोशिश-अंत लिखने के लिए हल किया जा सकता है, और पुनर्स्थापना-कोड को अंततः-ब्लॉक के अंदर डाल सकता है)।

एक बेहतर (और छोटा) समाधान है: बस मूल सूची की एक प्रति बनाएं। (यह भी एक ILI के बजाय एक पैरामीटर के रूप में IEnumerable का उपयोग करना संभव बनाता है):

static public IEnumerable<T> Swap2<T>(this IList<T> source, int index1, int index2)
{
    // Parameter checking is skipped in this example.

    // If nothing needs to be swapped, just return the original collection.
    if (index1 == index2)
        return source;

    // Make a copy.
    List<T> copy = source.ToList();

    // Swap the items.
    T temp = copy[index1];
    copy[index1] = copy[index2];
    copy[index2] = temp;

    // Return the copy with the swapped items.
    return copy;
}

इस समाधान का एक नुकसान यह है कि यह पूरी सूची की प्रतिलिपि बनाता है जो स्मृति का उपभोग करेगा और इससे समाधान धीमा हो जाता है।

आप निम्नलिखित समाधान पर विचार कर सकते हैं:

static public IEnumerable<T> Swap3<T>(this IList<T> source, int index1, int index2)
{
    // Parameter checking is skipped in this example.
    // It is assumed that index1 < index2. Otherwise a check should be build in and both indexes should be swapped.

    using (IEnumerator<T> e = source.GetEnumerator())
    {
        // Iterate to the first index.
        for (int i = 0; i < index1; i++)
            yield return source[i];

        // Return the item at the second index.
        yield return source[index2];

        if (index1 != index2)
        {
            // Return the items between the first and second index.
            for (int i = index1 + 1; i < index2; i++)
                yield return source[i];

            // Return the item at the first index.
            yield return source[index1];
        }

        // Return the remaining items.
        for (int i = index2 + 1; i < source.Count; i++)
            yield return source[i];
    }
}

और यदि आप इनपुट पैरामीटर IEnumerable होना चाहते हैं:

static public IEnumerable<T> Swap4<T>(this IEnumerable<T> source, int index1, int index2)
{
    // Parameter checking is skipped in this example.
    // It is assumed that index1 < index2. Otherwise a check should be build in and both indexes should be swapped.

    using(IEnumerator<T> e = source.GetEnumerator())
    {
        // Iterate to the first index.
        for(int i = 0; i < index1; i++) 
        {
            if (!e.MoveNext())
                yield break;
            yield return e.Current;
        }

        if (index1 != index2)
        {
            // Remember the item at the first position.
            if (!e.MoveNext())
                yield break;
            T rememberedItem = e.Current;

            // Store the items between the first and second index in a temporary list. 
            List<T> subset = new List<T>(index2 - index1 - 1);
            for (int i = index1 + 1; i < index2; i++)
            {
                if (!e.MoveNext())
                    break;
                subset.Add(e.Current);
            }

            // Return the item at the second index.
            if (e.MoveNext())
                yield return e.Current;

            // Return the items in the subset.
            foreach (T item in subset)
                yield return item;

            // Return the first (remembered) item.
            yield return rememberedItem;
        }

        // Return the remaining items in the list.
        while (e.MoveNext())
            yield return e.Current;
    }
}

Swap4 स्रोत की (सबसेट) की एक प्रति भी बनाता है। तो सबसे खराब स्थिति, यह फ़ंक्शन स्वेप 2 के रूप में धीमी और मेमोरी खपत है।


0

यदि आदेश मायने रखता है, तो आपको अपनी सूची में "टी" वस्तुओं पर एक संपत्ति रखनी चाहिए जो अनुक्रम को दर्शाती है। उन्हें स्वैप करने के लिए, बस उस संपत्ति के मूल्य को स्वैप करें, और फिर उस का उपयोग करें ।ort ( क्रम संपत्ति के साथ तुलना )


यदि आप वैचारिक रूप से इस बात से सहमत हो सकते हैं कि आपके टी में एक अंतर्निहित "आदेश" है, लेकिन यदि आप नहीं चाहते हैं कि यह एक अनियंत्रित फैशन में अंतर्निहित "आदेश" के बिना क्रमबद्ध हो, जैसे कि यूआई में।
डेव वान ने आईंड

@DaveVandenEynde, मैं एक काफी नौसिखिया प्रोग्रामर हूं, इसलिए कृपया मुझे माफ़ कर दें अगर यह एक बड़ा सवाल नहीं है: अगर सूची में ऑर्डर की अवधारणा शामिल नहीं है तो किसी सूची में आइटम स्वैप करने का क्या मतलब है?
मैथ्यू के।

@ MathieuK.well मेरा कहने का मतलब यह है कि यह उत्तर प्रस्तावित करता है कि ऑर्डर उन वस्तुओं पर संपत्ति से लिया गया है, जिस पर अनुक्रम को क्रमबद्ध किया जा सकता है । आमतौर पर ऐसा नहीं है।
डेव वान डेन आईंड

मैं इस जवाब को समझने के लिए (शायद गलत) कह रहा हूं, "अपनी वस्तुओं को नंबर दें, संख्याओं को एक संपत्ति में संग्रहीत करें। फिर, दो वस्तुओं को स्वैप करने के लिए, उनकी संख्या को स्वैप करें। इस संपत्ति द्वारा क्रमबद्ध सूची का उपयोग करें।"
मैथ्यू के।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.