जेनेरिक सूची - सूची के भीतर एक आइटम ले जाना


155

इसलिए मेरे पास एक सामान्य सूची है, और एक oldIndexऔर एक newIndexमूल्य है।

मैं आइटम oldIndexको newIndex... जितना संभव हो उतने पर स्थानांतरित करना चाहता हूं ।

कोई सुझाव?

ध्यान दें

आइटम को आइटम के बीच में समाप्त किया जाना चाहिए (newIndex - 1)और इसे हटाए जाने newIndex से पहले


1
आपको अपने टिक किए गए उत्तर को बदलना चाहिए। आपके साथ newIndex--आपके द्वारा कहा गया व्यवहार नहीं होता है।
मिरल

1
@ मिरल - आपको कौन सा उत्तर लगता है कि स्वीकार किया जाना चाहिए?
रिचर्ड ईव

4
jpierson की। यह उस ऑब्जेक्ट में परिणाम करता है जो इस कदम से पहले newIndex में होने से पहले पुरानेIndex पर हुआ करता था। यह कम से कम आश्चर्यजनक व्यवहार है (और जब मुझे कुछ drag'n'drop reordering कोड लिख रहा था तो इसकी आवश्यकता थी)। दी, वह बात कर रहा है ObservableCollectionऔर एक सामान्य नहीं है List<T>, लेकिन यह केवल परिणाम प्राप्त करने के लिए विधि कॉल को स्वैप करने के लिए तुच्छ है।
मिरल

अनुरोध किया (और सही ढंग से इस जवाब में लागू ) व्यवहार में आइटम के बीच में आइटम ले जाने के लिए [newIndex - 1]और [newIndex]उलटी नहीं जा सकती। Move(1, 3); Move(3, 1);प्रारंभिक अवस्था में सूची वापस नहीं करता है। इस बीच वहाँ अलग व्यवहार में प्रदान की जाती है ObservableCollectionऔर इस जवाब में बताया गया है, जो उलटी
लाइटमैन

जवाबों:


138

मुझे पता है कि आपने "सामान्य सूची" कहा था, लेकिन आपने यह निर्दिष्ट नहीं किया कि आपको सूची (टी) वर्ग का उपयोग करने की आवश्यकता है, इसलिए यहां कुछ अलग है।

ObservableCollection (टी) वर्ग एक है ले जाएँ विधि करता है कि आप क्या चाहते हैं।

public void Move(int oldIndex, int newIndex)

नीचे यह मूल रूप से इस तरह लागू किया गया है।

T item = base[oldIndex];
base.RemoveItem(oldIndex);
base.InsertItem(newIndex, item);

तो जैसा कि आप देख सकते हैं कि अदला-बदली विधि जो दूसरों ने सुझाई है वह अनिवार्य रूप से ऑब्जर्वेबल कॉलेक्शन है।

UPDATE 2015-12-30: आप अपने लिए अब रिफ्लेक्टर / ILSpy का उपयोग किए बिना Corefx में Move and MoveItem विधियों के लिए स्रोत कोड देख सकते हैं क्योंकि .NET खुला स्रोत है।


28
मुझे आश्चर्य है कि यह सूची <T> पर क्यों लागू नहीं किया गया है, इस पर कुछ प्रकाश डालने के लिए कोई भी?
एंड्रियास

जेनेरिक सूची और सूची (T) वर्ग के बीच क्या अंतर है? मैंने सोचा था कि वे समान थे :(
BKSpurgeon

एक "जेनेरिक सूची" का अर्थ .NET में डेटा संरचना जैसी किसी भी प्रकार की सूची या संग्रह हो सकता है, जिसमें ऑब्जर्वेबलकॉलेक्शन (T) या अन्य वर्ग शामिल हो सकते हैं जो ILI / ICollection / IEnumerable जैसे लिस्टी इंटरफ़ेस को लागू कर सकते हैं ।
1

6
क्या कोई समझा सकता है कि कृपया, वास्तव में कोई गंतव्य इंडेक्स शिफ्ट क्यों नहीं है (यदि यह स्रोत इंडेक्स से बड़ा है)?
व्लादिस

@vladius मेरा मानना ​​है कि विचार यह है कि निर्दिष्ट किए गए newIndex मान को केवल वांछित इंडेक्स निर्दिष्ट करना चाहिए कि आइटम को ले जाने के बाद होना चाहिए और चूंकि एक प्रविष्टि का उपयोग किया जा रहा है इसलिए समायोजन का कोई कारण नहीं है। अगर न्यूइंडेक्स मूल सूचकांक के सापेक्ष एक स्थिति थी जो एक और कहानी होगी जो मुझे लगता है लेकिन यह काम नहीं करता है।
जिपरसन

129
var item = list[oldIndex];

list.RemoveAt(oldIndex);

if (newIndex > oldIndex) newIndex--; 
// the actual index could have shifted due to the removal

list.Insert(newIndex, item);

9
यदि सूची में आइटम की दो प्रतियाँ हैं, तो पुराना समाधान होने से पहले आपका समाधान टूट जाता है। आपको यह सुनिश्चित करने के लिए RemoveAt का उपयोग करना चाहिए कि आपको सही मिल रहा है।
हारून मेंपा

6
दरअसल, डरपोक बढ़त मामला
गैरी शट्लर

1
@GarryShutler मैं यह नहीं देख सकता कि यदि हम हटा रहे हैं और फिर एक आइटम सम्मिलित कर रहे हैं तो सूचकांक कैसे बदल सकता है। newIndexवास्तव में घटने से मेरा परीक्षण टूट जाता है (नीचे मेरा उत्तर देखें)।
बेन फोस्टर

1
नोट: यदि थ्रेड-सुरक्षा महत्वपूर्ण है, तो यह सब एक lockबयान के अंदर होना चाहिए ।
rory.ap

1
मैं इसका उपयोग नहीं करूंगा क्योंकि यह कई कारणों से भ्रामक है। एक विधि को परिभाषित करना (एक सूची में पुराना और नया, मूव (newIndex)) और मूव (15,25) और फिर मूव (25,15) को कॉल करना एक पहचान नहीं है बल्कि स्वैप है। मूव (15,25) भी आइटम को इंडेक्स 24 पर ले जाता है, न कि 25 पर। स्वैपिंग के अलावा अस्थायी = आइटम [ओल्डइंडेक्स] द्वारा लागू किया जा सकता है; आइटम [oldindex] = आइटम [newindex]; आइटम [newindex] = अस्थायी; जो बड़े सरणियों पर अधिक कुशल लगता है। मूव (0,0) और मूव (0,1) भी वही होगा जो विषम भी है। और यह भी स्थानांतरित करें (0, गणना -1) आइटम को अंत तक स्थानांतरित नहीं करता है।
राउटर

12

मुझे पता है कि यह सवाल पुराना है, लेकिन मैंने जावास्क्रिप्ट कोड की इस प्रतिक्रिया को C # के लिए अनुकूलित किया है। आशा करता हूँ की ये काम करेगा

 public static void Move<T>(this List<T> list, int oldIndex, int newIndex)
{

    // exit if possitions are equal or outside array
    if ((oldIndex == newIndex) || (0 > oldIndex) || (oldIndex >= list.Count) || (0 > newIndex) ||
        (newIndex >= list.Count)) return;
    // local variables
    var i = 0;
    T tmp = list[oldIndex];
    // move element down and shift other elements up
    if (oldIndex < newIndex)
    {
        for (i = oldIndex; i < newIndex; i++)
        {
            list[i] = list[i + 1];
        }
    }
        // move element up and shift other elements down
    else
    {
        for (i = oldIndex; i > newIndex; i--)
        {
            list[i] = list[i - 1];
        }
    }
    // put element from position 1 to destination
    list[newIndex] = tmp;
}

9

सूची <T> .Remove () और सूची <T> .RemoveAt () उस आइटम को वापस न करें जिसे हटाया जा रहा है।

इसलिए आपको इसका उपयोग करना होगा:

var item = list[oldIndex];
list.RemoveAt(oldIndex);
list.Insert(newIndex, item);

5

वर्तमान में oldIndexहोने वाली आइटम डालें newIndexऔर फिर मूल उदाहरण को हटा दें।

list.Insert(newIndex, list[oldIndex]);
if (newIndex <= oldIndex) ++oldIndex;
list.RemoveAt(oldIndex);

आपको इस बात का ध्यान रखना होगा कि जिस आइटम को आप हटाना चाहते हैं उसका इंडेक्स प्रविष्टि के कारण बदल सकता है।


1
डालने से पहले आपको हटा देना चाहिए ... आपके आदेश से सूची का आवंटन हो सकता है।
जिम बाल्टर

4

मैंने सूची में आइटम ले जाने के लिए एक एक्सटेंशन विधि बनाई।

एक सूचकांक बदलाव नहीं होना चाहिए कि हम एक से बढ़ रहे हैं, तो मौजूदा आइटम हम एक के लिए एक आइटम आगे बढ़ रहे हैं के बाद से मौजूदा सूची में सूचकांक स्थिति।

किनारे का मामला जो @ ऑलिवर को संदर्भित करता है (किसी आइटम को सूची के अंत तक ले जाना) वास्तव में परीक्षणों को विफल करने का कारण होगा, लेकिन यह डिजाइन द्वारा है। सूची के अंत में एक नया आइटम सम्मिलित करने के लिए जिसे हम कहेंगे List<T>.Addlist.Move(predicate, list.Count) यह विफल हो जाना चाहिए क्योंकि यह सूचकांक स्थिति चाल से पहले मौजूद नहीं है।

किसी भी स्थिति में, मैंने दो अतिरिक्त एक्सटेंशन विधियाँ बनाई हैं, MoveToEndऔर MoveToBeginning, जिसका स्रोत यहाँ पाया जा सकता है

/// <summary>
/// Extension methods for <see cref="System.Collections.Generic.List{T}"/>
/// </summary>
public static class ListExtensions
{
    /// <summary>
    /// Moves the item matching the <paramref name="itemSelector"/> to the <paramref name="newIndex"/> in a list.
    /// </summary>
    public static void Move<T>(this List<T> list, Predicate<T> itemSelector, int newIndex)
    {
        Ensure.Argument.NotNull(list, "list");
        Ensure.Argument.NotNull(itemSelector, "itemSelector");
        Ensure.Argument.Is(newIndex >= 0, "New index must be greater than or equal to zero.");

        var currentIndex = list.FindIndex(itemSelector);
        Ensure.That<ArgumentException>(currentIndex >= 0, "No item was found that matches the specified selector.");

        // Copy the current item
        var item = list[currentIndex];

        // Remove the item
        list.RemoveAt(currentIndex);

        // Finally add the item at the new index
        list.Insert(newIndex, item);
    }
}

[Subject(typeof(ListExtensions), "Move")]
public class List_Move
{
    static List<int> list;

    public class When_no_matching_item_is_found
    {
        static Exception exception;

        Establish ctx = () => {
            list = new List<int>();
        };

        Because of = ()
            => exception = Catch.Exception(() => list.Move(x => x == 10, 10));

        It Should_throw_an_exception = ()
            => exception.ShouldBeOfType<ArgumentException>();
    }

    public class When_new_index_is_higher
    {
        Establish ctx = () => {
            list = new List<int> { 1, 2, 3, 4, 5 };
        };

        Because of = ()
            => list.Move(x => x == 3, 4); // move 3 to end of list (index 4)

        It Should_be_moved_to_the_specified_index = () =>
            {
                list[0].ShouldEqual(1);
                list[1].ShouldEqual(2);
                list[2].ShouldEqual(4);
                list[3].ShouldEqual(5);
                list[4].ShouldEqual(3);
            };
    }

    public class When_new_index_is_lower
    {
        Establish ctx = () => {
            list = new List<int> { 1, 2, 3, 4, 5 };
        };

        Because of = ()
            => list.Move(x => x == 4, 0); // move 4 to beginning of list (index 0)

        It Should_be_moved_to_the_specified_index = () =>
        {
            list[0].ShouldEqual(4);
            list[1].ShouldEqual(1);
            list[2].ShouldEqual(2);
            list[3].ShouldEqual(3);
            list[4].ShouldEqual(5);
        };
    }
}

कहाँ Ensure.Argumentपरिभाषित किया गया है?
ओलिवर

1
सामान्य तौर पर List<T>आप Insert(list.Count, element)सूची के अंत में कुछ रखने के लिए कह सकते हैं । तो अपने When_new_index_is_higherफोन चाहिए list.Move(x => x == 3, 5)जो वास्तव में विफल रहता है।
ऑलिवर

3
@ एक सामान्य में List<T>मैं सिर्फ एक सूची के अंत में एक नया आइटम .Addसम्मिलित करने के लिए कॉल करेंगे । जब हम केवल एक ही आइटम निकाल रहे हैं और इसे पुन: स्थापित कर रहे हैं, तो व्यक्तिगत आइटम को स्थानांतरित करते समय हम सूचकांक के मूल आकार को कभी नहीं बढ़ाते हैं। यदि आप मेरे उत्तर के लिंक पर क्लिक करते हैं तो आपको कोड मिल जाएगा । Ensure.Argument
बेन फोस्टर

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

1
@ वास्तव में यदि आप मेरे उत्तर को पढ़ते हैं, तो आइटम को सूची के अंत / शुरुआत में ले जाना समर्थित है। आप यहां चश्मा देख सकते हैं । हां मेरा कोड सूची के भीतर एक वैध (मौजूदा) स्थिति होने की उम्मीद करता है। हम आइटम ले जा रहे हैं , उन्हें सम्मिलित नहीं कर रहे हैं।
बेन फोस्टर

1

मैं या तो उम्मीद करेंगे:

// Makes sure item is at newIndex after the operation
T item = list[oldIndex];
list.RemoveAt(oldIndex);
list.Insert(newIndex, item);

... या:

// Makes sure relative ordering of newIndex is preserved after the operation, 
// meaning that the item may actually be inserted at newIndex - 1 
T item = list[oldIndex];
list.RemoveAt(oldIndex);
newIndex = (newIndex > oldIndex ? newIndex - 1, newIndex)
list.Insert(newIndex, item);

... चाल है, लेकिन मैं वी.एस. इस मशीन पर जाँच करने के लिए नहीं है।


1
@GarryShutler यह स्थिति पर निर्भर करता है। यदि आपका इंटरफ़ेस उपयोगकर्ता को सूची में स्थिति को सूचकांक द्वारा निर्दिष्ट करने की अनुमति देता है, तो वे भ्रमित हो जाएंगे जब वे आइटम को 15 से 20 तक जाने के लिए कहेंगे, लेकिन इसके बजाय यह 19 हो जाता है। यदि आपका इंटरफ़ेस उपयोगकर्ता को किसी आइटम को दूसरों के बीच खींचने की अनुमति देता है सूची में यह newIndexअगर बाद में है तो इसे कम करने के लिए समझ में आता है oldIndex
त्रिशूल

-1

सबसे सरल तरीका:

list[newIndex] = list[oldIndex];
list.RemoveAt(oldIndex);

संपादित करें

सवाल बहुत स्पष्ट नहीं है ... क्योंकि हमें परवाह नहीं है कि list[newIndex]आइटम कहां जाता है मुझे लगता है कि ऐसा करने का सबसे सरल तरीका निम्न है (विस्तार विधि के साथ या बिना):

    public static void Move<T>(this List<T> list, int oldIndex, int newIndex)
    {
        T aux = list[newIndex];
        list[newIndex] = list[oldIndex];
        list[oldIndex] = aux;
    }

यह समाधान सबसे तेज़ है क्योंकि इसमें सूची सम्मिलन / निष्कासन शामिल नहीं है।


4
यह NewIndex पर आइटम को अधिलेखित करेगा, सम्मिलित नहीं।
गैरी शट्लर

@ गैरी अंतिम परिणाम एक ही होने वाला नहीं है?
ओजगुर Ozcitak

4
नहीं, आप अंत में newIndex पर मान खो देंगे जो आपके द्वारा डाले जाने पर नहीं होगा।
गैरी शट्लर

-2

क्या अधिक सरल लोग ऐसा करते हैं

    public void MoveUp(object item,List Concepts){

        int ind = Concepts.IndexOf(item.ToString());

        if (ind != 0)
        {
            Concepts.RemoveAt(ind);
            Concepts.Insert(ind-1,item.ToString());
            obtenernombres();
            NotifyPropertyChanged("Concepts");
        }}

मूवडाउन के साथ भी ऐसा ही करें, लेकिन अगर "अगर (इंड! = कॉन्सेप्ट.काउंट ())" और कॉन्सेप्ट के लिए बदल दें। इंसर्ट (इंड + 1, आइटम। स्ट्रींग ());


-3

इस तरह से मैंने एक कदम तत्व विस्तार विधि लागू की। यह बहुत अच्छी तरह से तत्वों के लिए / बाद में और चरम सीमा तक आगे बढ़ रहा है।

public static void MoveElement<T>(this IList<T> list, int fromIndex, int toIndex)
{
  if (!fromIndex.InRange(0, list.Count - 1))
  {
    throw new ArgumentException("From index is invalid");
  }
  if (!toIndex.InRange(0, list.Count - 1))
  {
    throw new ArgumentException("To index is invalid");
  }

  if (fromIndex == toIndex) return;

  var element = list[fromIndex];

  if (fromIndex > toIndex)
  {
    list.RemoveAt(fromIndex);
    list.Insert(toIndex, element);
  }
  else
  {
    list.Insert(toIndex + 1, element);
    list.RemoveAt(fromIndex);
  }
}

2
यह फ्रांसिस्को के उत्तर का एक डुप्लिकेट है।
nivs1978
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.