最简单的方法重新排序一个序列

本文关键字:一个 方法 新排序 最简单 排序 | 更新日期: 2023-09-27 18:18:44

考虑4个(最多100个)类A的实例,在List中有一个int属性Order。

a0.Order is 0
a1.Order is 1
a2.Order is 2
a3.Order is 3

现在需要更改Order属性,以便a0插入在a3之前(或在列表中的任何索引-可以在开始,结束或中间)

结果应该是:

a0.Order is 2
a1.Order is 0
a2.Order is 1
a3.Order is 3

注意,这是一个列表,列表的实际物理顺序无关紧要,只有属性被改变了。

项目的数量不只是改变"顺序"。

实现这一点最简单的方法是什么?

最简单的方法重新排序一个序列

Order属性不应该是整数,而应该是小数。您可以轻松地更改该值,然后按其排序,以任意顺序检索条目。新值为前一值和后一值之间的中点。

注:然后,您只需更改列表中要重新定位的项目的Order属性。

你可以这样做:

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;
}

然后像这样调用它:

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 );

我们必须区分向上移动元素(到列表的末尾)还是向下移动元素(到列表的开始)。我们定义两个序号ik,其中i < k .

将元素i向上移动到k :
i以下和k以上的订单不受影响。
i+1 ... k的阶数减少一阶,i变为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;

移动元素ki :
i以下和k以上的订单不受影响。
i ... k-1的阶数增加1,k变为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;

如果你能负担得起,我会给它们编号,中间间隔:

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

通过这种方式,你可以简单地通过选择一个数字来重新排序。

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

经过一些迭代后,您可能在某些插入点上没有gabs。对于100件物品,您可以简单地重置所有订单号,使其具有相同的gabs。

是否可以将项目保存在列表中,并从列表中项目的物理顺序暗示Order属性?

给定一个没有 order属性的Item,如下所示:

class Item
{
    public readonly string Value;
    public Item(string value)
    {
        Value = value;
    }
    public override string ToString()
    {
        return Value;
    }
}

你可以为Items写一个简单的集合类,它有一个Move()方法,可以让你把一个项目从一个索引移动到另一个索引:

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();
    }
}

可以这样使用Items:

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.

然后当你需要Order时,你可以从列表的物理顺序中合成它,如下所示:

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

我为你编写了算法,它使用排序和LinkedList集合:

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();
        }
    }
}