1

I have a list class that implements IEnumerable<T>.
Type T is a complex class with a string member as unique identifier. But it's enough to take just int as element type for explanations.

I want to move multiple items one step to left.
Example:
Original list: 0, 1, 2, 3, 4, 5
Now all bold items (0,3,4) should be moved to left (as far as possible).
Result list: 0, 1, 3, 4, 2, 5

Is there a good algorithm to do that? Maybe just with LINQ.

Edit: Answers for a List<T> list are welcome. My class has similar methods. (Thanks for hint by TylerOhlsen.)
Edit2: One selected item should not pass another selected item.

thersch
  • 1,316
  • 12
  • 19
  • 1
    That's not really "as far as possible", do you mean to just move left one position? – lc. Nov 07 '12 at 15:36
  • 1
    @lc. One step to the left, as far as possible (e.g. `0` can't move) – Rawling Nov 07 '12 at 15:37
  • @lc. Element *0* is not moved left. That is what I meant to be "as far as possible". And in case item *1* would also be marked to be moved (beside item *0*) then this move would be also ignored. – thersch Nov 07 '12 at 15:39
  • 1
    This one looks like could help... http://stackoverflow.com/questions/2431971/move-item-up-in-ienumerable – activebiz Nov 07 '12 at 15:39
  • @Rawling wouldn't that yield a result like this: 0, 1, 3, 2, 4, 5? thersch do you want the list to look like this 0, 3, 4, 1, 2, 5 or 0, 1, 3, 4, 2, 5 (the one you provided). In the latter one why doesn't the 1 move? – Thomas Lindvall Nov 07 '12 at 15:44
  • @ThomasLindvall He wants the bolded items (0, 3, 4) in the list to be moved one left, if possible. – Rawling Nov 07 '12 at 15:46
  • You say it implements IEnumerable of T, but you also say it's a list. Can you have it implement IList of T instead so that we can take advantage of IndexOf, RemoveAt, and Insert? – TylerOhlsen Nov 07 '12 at 16:02
  • @ThomasLindvall I do not want to move bold items to start of list. I want only move **1 step** left. So if I try to move left items 0,1,3,4 the items 0 and 1 should be ignored because they are already on the most left side and items 3 and 4 should really be moved. So the result is 0,1,3,4,2,5. – thersch Nov 07 '12 at 16:05
  • @TylerOhlsen Our list has almost all methods that IList has. (It is older code before generics come to .NET.) So answers for IList would be appreciated. – thersch Nov 07 '12 at 16:10

4 Answers4

3

This looks like it works:

public static IEnumerable<T> MoveSelectedLeft<T>(
    this IEnumerable<T> source,
    IEnumerable<int> indicesToMove /* must be in order! */)
{
    using (var itm = indicesToMove.GetEnumerator())
    {
        bool hasNextToMove = itm.MoveNext();
        int nextToMove = hasNextToMove ? itm.Current : -1;

        bool canMoveYet = false;
        T held = default(T);
        int currentIndex = 0;

        foreach (T t in source)
        {
            if (hasNextToMove && nextToMove == currentIndex)
            {
                hasNextToMove = itm.MoveNext();
                nextToMove = hasNextToMove ? itm.Current : -1;
                yield return t;
            }
            else
            {
                if (!canMoveYet)
                {
                    canMoveYet = true;
                }
                else
                {
                    yield return held;
                }
                held = t;
            }

            currentIndex++;
        }

        if (canMoveYet)
            yield return held;
    }
}

called as

foreach (int i in new[] { 0,1,2,3,4,5 }.MoveSelectedLeft(new[] { 0,3,4 }))
{
    Console.WriteLine(i);
}
Rawling
  • 49,248
  • 7
  • 89
  • 127
2

I didn't come up with a good way to use Linq, but here's a simple looping algorithm. If you can come up with a Sort function, that would be better. Or you could look at the Linq Zip method.

IList<int> myList = new List<int>(new[] {0, 1, 2, 3, 4, 5});
IList<int> moveLeftList = new List<int>(new[] {0, 1, 3, 4});

//Go through the original list in order and remove
// all items from the move left list until you reach
// the first item that is not going to be moved left.
//Items removed are already as far left as they can be
// so they do not need to be moved left and can therefore
// be safely removed from the list to be moved.
foreach (int item in myList)
{
    int index = moveLeftList.IndexOf(item);

    if (index >= 0)
        moveLeftList.RemoveAt(index);
    else
        break;
}

foreach (int item in moveLeftList)
{
    int index = myList.IndexOf(item);

    //Dont move left if it is the first item in the list or it is not in the list
    if (index <= 0)
        continue;

    //Swap with this item with the one to its left
    myList.RemoveAt(index);
    myList.Insert(index-1, item);
}
TylerOhlsen
  • 5,485
  • 1
  • 24
  • 39
1

Here's a description of an algorithm. store the element directly to leftof your group of elements(in this case a 2) in a `temp variable.

starting at the leftmost element, move the elements to the left 1 by 1.

place the temp variable to the right of where the group now is.

If you're using a linked list, it can be better. you just remove the 2 from where it is, and insert it to the right of the group.

1

You should use the "Sort" function for this and provide a custom comparer.

Take a look at this example.

Joe
  • 2,496
  • 1
  • 22
  • 30