2012-03-05 9 views
15

コレクションからセットを削除するにはどうすればよいですが、削除されたアイテムは別のコレクションに残しておきますか?コレクションからアイテムを検索して削除する

私はこれを行う拡張メソッドを書いていますが、もっと良い方法があるはずです。ここに私の関数である。

public static List<T> FindAndRemove<T>(this List<T> lst, Predicate<T> match) 
{ 
    List<T> ret = lst.FindAll(match); 
    lst.RemoveAll(match); 
    return ret; 
} 

そして、あなたはこのようにそれを使用します。

List<String> myList = new List<String>(); 
myList.Add("ABC"); 
myList.Add("DEF"); 
myList.Add("ABC"); 
List<String> removed = myList.FindAndRemove(x => x == "ABC"); 
// myList now contains 1 item (DEF) 
// removed now contains 2 items (ABC, ABC) 

私はFindAllRemoveAll方法で舞台裏で何が100%わからないんだけど、私は想像しますより良い方法は、何らかの形で項目をあるリストから他のリストに「移す」ことです。

+1

あなたの解決策が最も効率的です。 –

+2

あなたの実装はうまくいくようです。コピーは.Netの安い操作なので、 "転送"の理由はありません(オブジェクトが一度に複数のコレクションに決して存在しないスレッド/例外の安全性を必要としない限り)。 – adrianm

+0

私は同意します。組み込みのLINQを使用することは、人生を楽にする目的です。シーンがMSによって選択された最良のソリューションになるということを考えています。あなたはC#で目には問題はないが、VBでは、コードスタイルを読むこととインラインで一致するように、クエリ 'return = lst.FindAll(match).RemoveAll(match)'をデイジーチェーンします。 – ppumkin

答えて

1

私はそれが最も効率的であるということに同意しません - リストの各要素に対して述語matchを2回呼び出しています。私はこのようにそれを行うだろう

:あなたのコレクションのサイズによって

var ret = new List<T>(); 
    var remaining = new List<T>(); 
    foreach (T t in lst) { 
     if (match(t)) 
     { 
      ret.Add(t); 
     } 
     else 
     { 
      remaining.Add(t); 
     } 
    } 
    lst.Clear(); 
    lst.AddRange(remaining); 
    return ret; 
+0

OMGだからC++の日..リストを繰り返しています。 – ppumkin

+0

リストを一度反復し、必要な結果を得るための作業を最小限に抑えます。何が好きではないですか? –

+1

@ppumkin:代わりに、foreach内のリストを変更することはできないので、見つかった結果を保持するリストを作成し、それ以降はそれを繰り返します。 – Guvante

0

、あなたはHashSetのではなく、リストとしてそれを実装する場合があります。十分に大きなコレクション(十分な大きさのコレクションでは、コレクションに含まれるものにいくらか依存しています)は、リストよりも自分自身の中でアイテムを見つけるのがずっと高速です。

9

Opの回答はこれまでに提案され、提案された解決策の中で最高です。私のマシンでのタイミングは次のとおりです。

public static class Class1 
{ 
    // 21ms on my machine 
    public static List<T> FindAndRemove<T>(this List<T> lst, Predicate<T> match) 
    { 
     List<T> ret = lst.FindAll(match); 
     lst.RemoveAll(match); 
     return ret; 
    } 

    // 538ms on my machine 
    public static List<T> MimoAnswer<T>(this List<T> lst, Predicate<T> match) 
    { 
     var ret = new List<T>(); 
     int i = 0; 
     while (i < lst.Count) 
     { 
      T t = lst[i]; 
      if (!match(t)) 
      { 
       i++; 
      } 
      else 
      { 
       lst.RemoveAt(i); 
       ret.Add(t); 
      } 
     } 
     return ret; 
    } 

    // 40ms on my machine 
    public static IEnumerable<T> GuvanteSuggestion<T>(this IList<T> list, Func<T, bool> predicate) 
    { 
     var removals = new List<Action>(); 

     foreach (T item in list.Where(predicate)) 
     { 
      T copy = item; 
      yield return copy; 
      removals.Add(() => list.Remove(copy)); 
     } 

     // this hides the cost of processing though the work is still expensive 
     Task.Factory.StartNew(() => Parallel.ForEach(removals, remove => remove())); 
    } 
} 

[TestFixture] 
public class Tester : PerformanceTester 
{ 
    [Test] 
    public void Test() 
    { 
     List<int> ints = Enumerable.Range(1, 100000).ToList(); 
     IEnumerable<int> enumerable = ints.GuvanteSuggestion(i => i % 2 == 0); 
     Assert.That(enumerable.Count(), Is.EqualTo(50000)); 
    } 
} 
+0

タイミングを提供してくれてありがとう –

0

オリジナルのリストを2つの新しいリストに分割します。実装は、リストだけでなく、IEnumerableでも動作し、ソースが不変であると仮定する必要があります。 パーティショニングについてのこの記事を参照してください: LINQ Partition List into Lists of 8 members 私はMoreLinqが既にカバーしていると思います。

関連する問題