开发者

Move item up in IEnumerable

开发者 https://www.devze.com 2022-12-22 22:19 出处:网络
I have a need to move an item in an IEnumerable<> up, that is move one item above another. What is the simplest way to do this?

I have a need to move an item in an IEnumerable<> up, that is move one item above another. What is the simplest way to do this?

A similar quest开发者_运维技巧ion was asked here but I don't have a generic list only an IEnumerable<>: Generic List - moving an item within the list


As @Brian commented the question is a little unclear as to what move an item in an IEnumerable<> up means.

If you want to reorder an IEnumerable for a single item then the code below should might be what you are looking for.

public static IEnumerable<T> MoveUp<T>(this IEnumerable<T> enumerable, int itemIndex)
{
    int i = 0;

    IEnumerator<T> enumerator = enumerable.GetEnumerator();
    while (enumerator.MoveNext())
    {
        i++;

        if (itemIndex.Equals(i))
        {
            T previous = enumerator.Current;

            if (enumerator.MoveNext())
            {
                yield return enumerator.Current;
            }

            yield return previous;

            break;
        }

        yield return enumerator.Current;
    }

    while (enumerator.MoveNext())
    {
        yield return enumerator.Current;
    }
}


You can't. IEnumerable is only to iterate through some items, not for editing a list of items


You can use the ToList() extension method and use the answer from the question you referenced. e.g.

var list = enumerable.ToList();
//do stuff from other answer, and then convert back to enumerable if you want
var reorderedEnumerable = list.AsEnumerable();


I didn't find anything that would do what you want with IEnumerable<T>. Having developed similar stuff in the past for specific types of collections, list, arrays, etc, I felt it was time to take a better look at it. So I took a couple of minutes to write a generic version that could be applied to any IEnumerable<T>.

I did some basic testing and parameter checking but by no means consider them compreensive. Given that disclaimer, let's get to the code:

static class Enumerable {
    public static IEnumerable<T> MoveDown<T>(this IEnumerable<T> source, int index) {
        if (source == null) {
            throw new ArgumentNullException("source");
        }
        T[] array = source.ToArray();
        if (index == array.Length - 1) {
            return source;
        }
        return Swap<T>(array, index, index + 1);
    }

    public static IEnumerable<T> MoveDown<T>(this IEnumerable<T> source, T item) {
        if (source == null) {
            throw new ArgumentNullException("source");
        }
        T[] array = source.ToArray();
        int index = Array.FindIndex(array, i => i.Equals(item));
        if (index == -1) {
            throw new InvalidOperationException();
        }
        if (index == array.Length - 1) {
            return source;
        }
        return Swap<T>(array, index, index + 1);
    }

    public static IEnumerable<T> MoveUp<T>(this IEnumerable<T> source, int index) {
        if (source == null) {
            throw new ArgumentNullException("source");
        }
        T[] array = source.ToArray();
        if (index == 0) {
            return source;
        }
        return Swap<T>(array, index - 1, index);
    }

    public static IEnumerable<T> MoveUp<T>(this IEnumerable<T> source, T item) {
        if (source == null) {
            throw new ArgumentNullException("source");
        }
        T[] array = source.ToArray();
        int index = Array.FindIndex(array, i => i.Equals(item));
        if (index == -1) {
            throw new InvalidOperationException();
        }
        if (index == 0) {
            return source;
        }
        return Swap<T>(array, index - 1, index);
    }

    public static IEnumerable<T> Swap<T>(this IEnumerable<T> source, int firstIndex, int secondIndex) {
        if (source == null) {
            throw new ArgumentNullException("source");
        }
        T[] array = source.ToArray();
        return Swap<T>(array, firstIndex, secondIndex);
    }

    private static IEnumerable<T> Swap<T>(T[] array, int firstIndex, int secondIndex) {
        if (firstIndex < 0 || firstIndex >= array.Length) {
            throw new ArgumentOutOfRangeException("firstIndex");
        }
        if (secondIndex < 0 || secondIndex >= array.Length) {
            throw new ArgumentOutOfRangeException("secondIndex");
        }
        T tmp = array[firstIndex];
        array[firstIndex] = array[secondIndex];
        array[secondIndex] = tmp;
        return array;
    }

    public static IEnumerable<T> Swap<T>(this IEnumerable<T> source, T firstItem, T secondItem) {
        if (source == null) {
            throw new ArgumentNullException("source");
        }
        T[] array = source.ToArray();
        int firstIndex = Array.FindIndex(array, i => i.Equals(firstItem));
        int secondIndex = Array.FindIndex(array, i => i.Equals(secondItem));
        return Swap(array, firstIndex, secondIndex);
    }
}

As you can see, MoveUp and MoveDown are basically Swap operations. With MoveUp you swap positions with the previous element and with MoveDown you swap positions with the next element. Of course, that does not apply for moving up the first element or moving down the last element.

Running a quick test with the code below...

class Program {
    static void Main(string[] args) {
        int[] a = { 0, 2, 1, 3, 4 };
        string[] z = { "Zero", "Two", "One", "Three", "Four" };
        IEnumerable<int> b = Enumerable.Swap(a, 1, 2);
        WriteAll(b);
        IEnumerable<int> c = Enumerable.MoveDown(a, 1);
        WriteAll(c);
        IEnumerable<int> d = Enumerable.MoveUp(a, 2);
        WriteAll(d);
        IEnumerable<int> f = Enumerable.MoveUp(a, 0);
        WriteAll(f);
        IEnumerable<int> g = Enumerable.MoveDown(a, 4);
        WriteAll(g);
        IEnumerable<string> h = Enumerable.Swap(z, "Two", "One");
        WriteAll(h);
        var i = z.MoveDown("Two");
        WriteAll(i);
        var j = z.MoveUp("One");
        WriteAll(j);
        Console.WriteLine("Press any key to continue...");
        Console.Read();
    }

    private static void WriteAll<T>(IEnumerable<T> b) {
        foreach (var item in b) {
            Console.WriteLine(item);
        }
    }

... it looks like everything is working well.

I hope it serves at least as a starting point for you.


I like this approach

    /// <summary>
/// Extension methods for <see cref="System.Collections.Generic.List{T}"/>
/// </summary>
public static class ListExtensions
{
    public static void MoveForward<T>(this List<T> list, Predicate<T> itemSelector, bool isLastToBeginning)
    {
        Ensure.ArgumentNotNull(list, "list");
        Ensure.ArgumentNotNull(itemSelector, "itemSelector");

        var currentIndex = list.FindIndex(itemSelector);

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

        bool isLast = list.Count - 1 == currentIndex;

        if (isLastToBeginning && isLast)
        {
            // Remove the item
            list.RemoveAt(currentIndex);

            // add the item to the beginning
            list.Insert(0, item);
        }
        else if (!isLast)
        {
            // Remove the item
            list.RemoveAt(currentIndex);

            // add the item at next index
            list.Insert(currentIndex + 1, item);
        }
    }

    public static void MoveBack<T>(this List<T> list, Predicate<T> itemSelector, bool isFirstToEnd)
    {
        Ensure.ArgumentNotNull(list, "list");
        Ensure.ArgumentNotNull(itemSelector, "itemSelector");

        var currentIndex = list.FindIndex(itemSelector);

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

        bool isFirst = 0 == currentIndex;

        if (isFirstToEnd && isFirst)
        {
            // Remove the item
            list.RemoveAt(currentIndex);

            // add the item to the end
            list.Add(item);             
        }
        else if (!isFirstToEnd)
        {
            // Remove the item
            list.RemoveAt(currentIndex);

            // add the item to previous index
            list.Insert(currentIndex - 1, item);
        }
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消