有效地对IList <t>进行排序而不复制源列表</t>

时间:2011-07-12 07:10:38

标签: c# .net collections

鉴于下面的测试用例,我该如何:

  1. 根据匹配的IList<TestObject>索引对Id进行排序 在IList<int>列表中。
  2. 不匹配的值将移至列表末尾并按其原始索引排序。在这种情况下,由于索引列表中不存在3和4,因此我们希望看到list[3] == 3list[4] == 4
  3. 虽然我知道这可以通过linq实现,但我需要使用原始列表而不是创建一个新列表(由于列表的存储方式)。
  4. 源列表必须是IList(我不能使用List<T>
  5. 以下是测试:

        public class TestObject
        {
            public int Id { get; set; }
        }
    
        [Test]
        public void Can_reorder_using_index_list()
        {
            IList<TestObject> list = new List<TestObject>
            {
                new TestObject { Id = 1 },
                new TestObject { Id = 2 },
                new TestObject { Id = 3 },
                new TestObject { Id = 4 },
                new TestObject { Id = 5 }
            };
    
            IList<int> indexList = new[] { 10, 5, 1, 9, 2 };
    
            // TODO sort
    
            Assert.That(list[0].Id, Is.EqualTo(5));
            Assert.That(list[1].Id, Is.EqualTo(1));
            Assert.That(list[2].Id, Is.EqualTo(2));
            Assert.That(list[3].Id, Is.EqualTo(3));
            Assert.That(list[4].Id, Is.EqualTo(4));
        }
    

    更新

    根据要求,这是我尝试过的,但1)它只适用于List<T>和2)我不确定它是最有效的方式:

           var clone = list.ToList();
            list.Sort((x, y) =>
            {
                var xIndex = indexList.IndexOf(x.Id);
                var yIndex = indexList.IndexOf(y.Id);
    
                if (xIndex == -1)
                {
                    xIndex = list.Count + clone.IndexOf(x);
                }
                if (yIndex == -1)
                {
                    yIndex = list.Count + clone.IndexOf(y);
                }
    
                return xIndex.CompareTo(yIndex);
            });
    

    更新2:

    感谢@leppie,@ jamiec,@ maych wheat - 这是工作代码:

        public class TestObjectComparer : Comparer<TestObject>
        {
            private readonly IList<int> indexList;
            private readonly Func<TestObject, int> currentIndexFunc;
            private readonly int listCount;
    
            public TestObjectComparer(IList<int> indexList, Func<TestObject, int> currentIndexFunc, int listCount)
            {
                this.indexList = indexList;
                this.currentIndexFunc = currentIndexFunc;
                this.listCount = listCount;
            }
    
            public override int Compare(TestObject x, TestObject y)
            {
                var xIndex = indexList.IndexOf(x.Id);
                var yIndex = indexList.IndexOf(y.Id);
    
                if (xIndex == -1)
                {
                    xIndex = listCount + currentIndexFunc(x);
                }
                if (yIndex == -1)
                {
                    yIndex = listCount + currentIndexFunc(y);
                }
    
                return xIndex.CompareTo(yIndex);
            }
        }
    
        [Test]
        public void Can_reorder_using_index_list()
        {
            IList<TestObject> list = new List<TestObject>
            {
                new TestObject { Id = 1 },
                new TestObject { Id = 2 },
                new TestObject { Id = 3 },
                new TestObject { Id = 4 },
                new TestObject { Id = 5 }
            };
    
            IList<int> indexList = new[] { 10, 5, 1, 9, 2, 4 };
    
            ArrayList.Adapter((IList)list).Sort(new TestObjectComparer(indexList, x => list.IndexOf(x), list.Count));
    
            Assert.That(list[0].Id, Is.EqualTo(5));
            Assert.That(list[1].Id, Is.EqualTo(1));
            Assert.That(list[2].Id, Is.EqualTo(2));
            Assert.That(list[3].Id, Is.EqualTo(3));
            Assert.That(list[4].Id, Is.EqualTo(4));
        }
    

3 个答案:

答案 0 :(得分:2)

您可以尝试以下操作:

ArrayList.Adapter(yourilist).Sort();

<强>更新

通用比较器:

class Comparer<T> : IComparer<T>, IComparer
{
  internal Func<T, T, int> pred;

  public int Compare(T x, T y)
  {
    return pred(x, y);  
  }

  public int Compare(object x, object y)
  {
    return Compare((T)x, (T)y);
  }
}

static class ComparerExtensions
{
  static IComparer Create<T>(Func<T, T, int> pred)
  {
    return new Comparer<T> { pred = pred };
  }

  public static void Sort<T>(this ArrayList l, Func<T, T, int> pred)
  {
    l.Sort(Create(pred));
  }
}

<强>用法:

ArrayList.Adapter(list).Sort<int>((x,y) => x - y);

答案 1 :(得分:2)

正在考虑这一点,事实上如前所述,你需要ArrayList.Adapter,但是你会注意到它需要一个非通用的IList,所以需要一些强制转换:

ArrayList.Adapter((IList)list)

您还需要编写一个比较器,其中包含进行排序的逻辑。请原谅,但是:

public class WeirdComparer : IComparer,IComparer<TestObject>
{
    private IList<int> order;
    public WeirdComparer(IList<int> order)
    {
        this.order = order;
    }
    public int Compare(object x, object y)
    {
        return Compare((TestObject) x, (TestObject) y);
    }

    public int Compare(TestObject x, TestObject y)
    {
        if(order.Contains(x.Id))
        {
            if(order.Contains(y.Id))
            {
                return order.IndexOf(x.Id).CompareTo(order.IndexOf(y.Id));    
            }
            return -1;
        }
        else
        {
            if (order.Contains(y.Id))
            {
                return 1;
            }
            return x.Id.CompareTo(y.Id);
        }
    }
}

编辑:在上面的comparerr中添加了实现

然后用法如下:

IList<int> indexList = new[] { 10, 5, 1, 9, 2 };
ArrayList.Adapter((IList)list).Sort(new WeirdComparer(indexList));

顺便说一句,this thread解释了一种很好的方法,可以将其转换为扩展方法,使您的代码更易于重复使用,更容易阅读IMO。

答案 2 :(得分:0)

这是比较器的通用版本。 IEntity<TId>只是一个简单的界面,其属性“Id”的类型为TId

public class IndexComparer<T, TId> : Comparer<T> where T : IEntity<TId> where TId : IComparable
{
    private readonly IList<TId> order;
    private readonly int listCount;
    private readonly Func<T, int> currentIndexFunc;

    public IndexComparer(Func<T, int> currentIndexFunc, IList<TId> order, int listCount) {
        this.order = order;
        this.listCount = listCount;
        this.currentIndexFunc = currentIndexFunc;
    }

    public override int Compare(T x, T y)
    {
        var xIndex = order.IndexOf(x.Id);
        var yIndex = order.IndexOf(y.Id);

        if (xIndex == -1)
        {
            xIndex = listCount + currentIndexFunc(x);
        }
        if (yIndex == -1)
        {
            yIndex = listCount + currentIndexFunc(y);
        }

        return xIndex.CompareTo(yIndex);
    }
}

工作测试:

[TestFixture]
public class OrderingTests
{
    public class TestObject : IEntity<int>
    {
        public int Id { get; set; }
    }

    [Test]
    public void Can_reorder_using_index_list()
    {
        IList<TestObject> list = new List<TestObject>
        {
            new TestObject { Id = 1 },
            new TestObject { Id = 2 },
            new TestObject { Id = 3 },
            new TestObject { Id = 4 },
            new TestObject { Id = 5 }
        };

        IList<int> indexList = new[] { 10, 5, 1, 9, 2, 4 };
        ArrayList.Adapter((IList)list)
            .Sort(new IndexComparer<TestObject, int>(x => list.IndexOf(x), indexList, list.Count));

        Assert.That(list[0].Id, Is.EqualTo(5));
        Assert.That(list[1].Id, Is.EqualTo(1));
        Assert.That(list[2].Id, Is.EqualTo(2));
        Assert.That(list[3].Id, Is.EqualTo(4));
        Assert.That(list[4].Id, Is.EqualTo(3));
    }
}

这符合我原始问题中列出的要求。不匹配的元素移动到列表的末尾,然后按原始索引排序。