score:7

Accepted answer

The Order property should not be an integer but a decimal. You can easily change the value and then sort by it to retrieve the items in any arbitrary order. The new value is the midpoint between the preceding and following values.

P.S. Then you only have to change the Order property for the item(s) in the list you want to reposition.

score:1

If you can afford to I would number them with gaps in between:

a0.Order is 10
a1.Order is 20
a2.Order is 30
a3.Order is 40

This way you can simple reorder by picking a number within a gab.

a0.Order is 35
a1.Order is 20
a2.Order is 30
a3.Order is 40

After some iterations you may have no gabs left at some insertion point. With 100 items you can simple reset all the order numbers to have equal gabs again.

score:7

You could do something like this:

void MoveAndUpdateOrder(List<A> list, A item, int positionToInsert)
{
    // Order elements
    var ordered_list = list.OrderBy(a => a.Order).ToList();

    // Remove and insert at the proper position
    ordered_list.Remove(item);
    ordered_list.Insert(positionToInsert, item);

    // Update the Order properties according to it's current index
    for ( int i = 0; i < ordered_list.Count; i++ )
        ordered_list[i].Order = i;
}

And then call it like this:

var a0 = new A { Order = 0 };
var a1 = new A { Order = 1 };
var a2 = new A { Order = 2 };
var a3 = new A { Order = 3 };

var list = new List<A>
{
    a0, a1, a2, a3
};

MoveAndUpdateOrder( list, a0, 2 );

score:3

We have to differentiate between moving an element upwards (to the end) or downwards (to the beginning of the list). Let's define two order numbers i and k where i < k.

Move element i upwards to k:
The orders below i and above k are not affected.
The orders i+1 ... k are decreased by one and i becomes k.

A moving = list.Where(a => a.Order == i);
foreach (A x in list.Where(a => a.Order > i && a.Order <= k)
{
    x.Order--;
}
moving.Order = k;

Move element k downwards to i:
The orders below i and above k are not affected.
The orders i ... k-1 are increased by one and k becomes i.

A moving = list.Where(a => a.Order == k);
foreach (A x in list.Where(a => a.Order >= i && a.Order < k)
{
    x.Order++;
}
moving.Order = i;

score:0

Could you just keep the items in a list and imply the Order property from the physcial order of the items in the list?

Given an Item class without an order property, like so:

class Item
{
    public readonly string Value;

    public Item(string value)
    {
        Value = value;
    }

    public override string ToString()
    {
        return Value;
    }
}

You could write a simple collection class for Items which has a Move() method to let you move an item from one index to another:

class Items: IEnumerable<Item>
{
    private readonly List<Item> _items = new List<Item>();

    public void Add(Item item)
    {
        _items.Add(item);
    }

    public int Count
    {
        get { return _items.Count; }
    }

    public void Move(int oldIndex, int newIndex)
    {
        Item item = _items[oldIndex];
        _items.RemoveAt(oldIndex);
        _items.Insert(newIndex, item);
    }

    IEnumerator<Item> IEnumerable<Item>.GetEnumerator()
    {
        return _items.GetEnumerator();
    }

    public IEnumerator GetEnumerator()
    {
        return _items.GetEnumerator();
    }
}

You can use Items like so:

var items = new Items
{
    new Item("a0"),
    new Item("a1"),
    new Item("a2"),
    new Item("a3")
};

// ...

items.Move(0, 2); // Move the item at index 0 to index 2.

Then when you need the Order you can synthesize it from the list's physical order like so:

var orderedItems = items.Select((item, index) => new { Item = item, Order = index});

score:0

I wrote the algorithm for You, that uses sorting and LinkedList collection:

using System;
using System.Collections.Generic;

namespace OrderExample {

    public class A {
        public int Order { get; set; } 
    }

    public class Program {

        // Change order so that a is ordered between b and c.
        public static void SetOrder(List<A> list, A a, A b, A c) {
            list.Sort((x, y) => x.Order.CompareTo(y.Order));
            var linkedList = new LinkedList<A>(list);
            var bNode = linkedList.Find(b);
            if (bNode != null) {
                linkedList.Remove(a);
                linkedList.AddAfter(bNode, a);
                var i = 0;
                foreach (var value in linkedList) {
                    value.Order = i++;
                }                
            }
        }

        static void Main() {
            var a0 = new A {Order = 0};
            var a1 = new A {Order = 1};
            var a2 = new A {Order = 2};
            var a3 = new A {Order = 3};
            var list = new List<A> {a0, a1, a2, a3};

            SetOrder(list, a0, a2, a3);
            foreach (var a in list) {
                Console.Out.WriteLine(a.Order);
            }
            Console.ReadKey();
        }
    }
}

Related Articles