2011-07-12 3 views
7

をコピーせずにIListを を並べ替えるどのようにすることができます: が効率的に下のテストケースを考えるとソースリスト

  1. ソートIList<int>リスト内の一致Id の指標に基づいてIList<TestObject>
  2. 不一致の値はリストの最後に移動され、元のインデックスでソートされます。この場合、3と4はインデックスリストに存在しないため、list[3] == 3list[4] == 4と表示されます。
  3. 私はこれがlinqで達成できることは分かっていますが、私はオリジナルののリストを作成する必要があります(リストがどのように格納されるかによって)。
  4. ソースリストはIListここ

はテストだ(私はList<T>を使用することはできません)でなければなりません:

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、@mitch小麦へ

おかげ - これは作業コードです:

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)); 
    } 
+0

@Mitch、私は私の質問を更新しました。私はそれが働いているだけで 'List ' –

+0

@Ben - あなたのIListのインプレースソーティングのための私の答えを参照し、潜在的により効率的な比較方法...いずれも特に遅いとは思わないが思う! – Jamiec

答えて

2

は、しかし、あなたはそれがいくつかのように非ジェネリックのIListかかりますよ、あなたはArrayList.Adapterを必要として、ビットのためにこれを見て、そして実際に、以前に言われて

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

EDIT:上記comparerrに実装

次のように続いての使用が可能です追加しました:ところで

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

を、this threadがいいの説明これを拡張メソッドに変えてコードを再利用しやすくし、IMOを読みやすくします。

+0

+1 - ArrayList.Adapterの非総称的な要求によってキャッチされ、あなたの答えを見た:) –

+0

私は自分の作業コードを追加しました。 –

+1

コンテナが 'IList'(必ずしも真ではない)を実装していることを前提とすると、' ArrayList.Adapter'の 'Sort'はリスト内の項目を新しい配列にコピーし、ソートしてリストに戻します。質問タイトルがソースリストのコピーを避けるように頼んだとき、なぜこれが受け入れられたのか分かりません。これがうまくいくすべてのシナリオで、プレーンな配列に自分自身をコピーするだけで、同じように動作し、強く型付けされたものがすべて保持され、無意味なボクシングを避けることができます。 – hvd

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

"ArrayListクラスは、IListでこれらのメソッドを使用する手段である可能性がありますが、ラッパーを使用してこれらの汎用操作を実行すると、IListに直接適用される操作よりも効率が悪くなる可能性があります。 http://msdn.microsoft.com/en-us/library/system.collections.arraylist.adapter.aspx –

+0

@leppie、とにかくこれを使用して、代理人の比較者を渡すのですか?私はIComparerを作成することができましたが、現在のインデックスを読むためにソースリストにアクセスする必要があります。私はちょうど私が試みたもので私の質問を更新しました。 –

+0

@Mitch Wheat:「IListに直接適用される操作よりも効率が悪いかもしれない」とは、説明なしで役に立たない。あまり効果的でないのは、余分なレベルの間接指定だけです。 – leppie

0

ここに、比較元の一般的なバージョンがあります。

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

これは私の元の質問に概説されているような要件を満たしているIEntity<TId>はタイプTIdのプロパティ「ID」を持っているだけのシンプルなインタフェースです。一致しない要素はリストの最後に移動され、元のインデックス順に並べ替えられます。

関連する問題