在不复制源列表的情况下有效地对IList 进行排序
鉴于下面的测试用例我怎么能:
- 根据
IList
列表中匹配Id
的索引对IList
排序。 - 不匹配的值将移动到列表的末尾,并按其原始索引进行排序。 在这种情况下,由于索引列表中不存在3和4,我们希望看到
list[3] == 3
和list[4] == 4
。 - 虽然我知道这可以通过linq实现,但我需要求助于原始列表而不是创建一个新列表(由于列表的存储方式)。
- 源列表必须是
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)); } }
这符合我原始问题中列出的要求。 不匹配的元素将移动到列表的末尾,然后按其原始索引排序。