在不复制源列表的情况下有效地对IList 进行排序

鉴于下面的测试用例我怎么能:

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

这是测试:

  public class TestObject { public int Id { get; set; } } [Test] public void Can_reorder_using_index_list() { IList list = new List { new TestObject { Id = 1 }, new TestObject { Id = 2 }, new TestObject { Id = 3 }, new TestObject { Id = 4 }, new TestObject { Id = 5 } }; IList 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和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,@ mitch wheat – 这是工作代码:

  public class TestObjectComparer : Comparer { private readonly IList indexList; private readonly Func currentIndexFunc; private readonly int listCount; public TestObjectComparer(IList indexList, Func 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 list = new List { new TestObject { Id = 1 }, new TestObject { Id = 2 }, new TestObject { Id = 3 }, new TestObject { Id = 4 }, new TestObject { Id = 5 } }; IList 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)); } 

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

 ArrayList.Adapter((IList)list) 

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

 public class WeirdComparer : IComparer,IComparer { private IList order; public WeirdComparer(IList 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 indexList = new[] { 10, 5, 1, 9, 2 }; ArrayList.Adapter((IList)list).Sort(new WeirdComparer(indexList)); 

顺便说一句, 这个线程解释了一个很好的方法,将其转换为一个扩展方法,这将使您的代码更可重用,更容易阅读IMO。

您可以尝试以下方法:

 ArrayList.Adapter(yourilist).Sort(); 

更新:

通用比较器:

 class Comparer : IComparer, IComparer { internal Func 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(Func pred) { return new Comparer { pred = pred }; } public static void Sort(this ArrayList l, Func pred) { l.Sort(Create(pred)); } } 

用法:

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

这是比较器的通用版本。 IEntity只是一个简单的接口,具有类型为TId的属性“Id”:

 public class IndexComparer : Comparer where T : IEntity where TId : IComparable { private readonly IList order; private readonly int listCount; private readonly Func currentIndexFunc; public IndexComparer(Func currentIndexFunc, IList 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 { public int Id { get; set; } } [Test] public void Can_reorder_using_index_list() { IList list = new List { new TestObject { Id = 1 }, new TestObject { Id = 2 }, new TestObject { Id = 3 }, new TestObject { Id = 4 }, new TestObject { Id = 5 } }; IList indexList = new[] { 10, 5, 1, 9, 2, 4 }; ArrayList.Adapter((IList)list) .Sort(new IndexComparer(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)); } } 

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