2011-10-25 12 views
-4

haloa!再帰的取得ID

私はChannelがoffcourseカスタムクラスですList<Channel>ありますように

ID = 1, parentID = 0 
    ID = 64, parentID = 1 
     ID = 28, parentID = 64 
     ID = 36, parentID = 64 
ID = 5, parentID = 0 

と:

public class Channel 
{ 
    public long ID { get; set; } 
    public long parentID { get; set; } 
} 

strcutureのようなものすることができます。

私は何をしたいのは、特定のチャンネルのすべての子供のIDの取得である:あなたのクラスで

List<Channel> list = new List<Channel>(); 
List<long> ret = new List<long>(); 

を持つ

function List<long> getChildrenIDS (long ID) 
{ 
    // WHAT GOES HERE? 
} 
+2

また、子供の子供も欲しいですか?サンプルデータに別のレベルを追加して、希望の出力をリストアップしてください。 –

+0

ID28が繰り返されていますが、これは間違いですか? –

+2

'Channel'が' List 'を持っていればこれを動作させるほうがはるかに簡単ではないでしょうか? Children {get;プライベートセット; } '? – Oliver

答えて

2

foreach(long childID in GetChildren(list, id))のように実際には必要のないリストを作成しないと、メモリと時間のパフォーマンスが向上します。

我々は(あなたの質問のタイトルごとなど)再帰のいずれかの使用を作ることができる唯一のケースであるすべての子孫(子、孫、ひ孫など)を取得するために使用:

重複があることができないと仮定すると、 (複数の経路を通じて同じ孫):

private IEnumerable<long> GetDescendants(List<Channel> list, long id) 
{ 
    foreach(long child in GetChildren(list, id)) 
    { 
    yield return child; 
    foreach(long grandchild in GetDescendants(list, child)) 
     yield return grandchild; 
    } 
} 

重複があることができれば、あなたは上記に.Distinct()を適用するかのために行くことができる:

private IEnumerable<long> GetDescHelper(List<Channel> list, long id, HashSet<long> already) 
{ 
    foreach(long child in GetChildren(list, id)) 
    if(already.Add(child)) 
    { 
     yield return child; 
     foreach(long desc in GetDescHelper(list, child, already)) 
     yield return desc; 
    } 
} 
public IEnumerable<long> GetDescendants(List<Channel> list, long id) 
{ 
    return GetDescHelper(list, id, new HashSet<long>()); 
} 

これは、Channelクラスに子供のList<Channel>を維持させることによって、これをモデル化すると思います。

0

を、あなたが行うことができます:

ません再帰(子供のみ)

private List<long> GetChildrenIds(long parentId) 
{ 
    list.ForEach(p => { if (p.parentID == parentId) ret.Add(p.ID); }); 
    return ret; 
} 

または再帰とマルコスの答えは本当に望ましい結果を生成する場合(あまりにも子供たちの子供たち)

private List<long> GetChildrenIds(long parentId) 
{ 
    list.ForEach(p => { if (p.parentID == parentId) { 
          ret.Add(p.ID); 
          GetChildrenIds(p.ID); 
         } 
         }); 
    return ret; 
} 
+0

@Dementic:編集したコードを見てください。これはあなたが必要とするものですか? – Marco

+0

あなたのクラスでretを取得すると、並行処理の問題(2つのスレッドが同じリストに追加される)が発生し、メソッドが2回呼び出されたときにretが決してクリアされないため、両方の呼び出しの結果が返されるバグが発生します。 retはクラスとは何の関係もないので、特定の呼び出しでのみ使用されるため、メソッド自体で作成されたローカルでなければなりません。 –

1

は知らないが、私はより多くのLINQishな方法でこれを記述します。

private IEnumerable<long> GetChildrenIds(IEnumerable<Channel> channels, long parentId) 
{ 
    if(channels == null) 
     throw new ArgumentNullException("channels"); 

    var childs = channels.Where(c => c.ParentId == parentId) 
         .Select(c => c.Id); 

    return childs; 
} 

あなたの場合

private IEnumerable<long> GetAllChildrenIds(IEnumerable<Channel> channels, long parentId) 
{ 
    var childs = GetChildrenIds(channels, parentId); 
    var alldescendants = childs.SelectMany(id => GetAllChildrenIds(channels, id)); 

    return childs.Concat(alldescendants); 
} 

しかし、それは巡回冗長をチェックしませんし、stackoverflowの例外に終わる可能性があることに注意してください:また、あなたは多分、この機能を使用することができ、深い入れ子になったものを必要とします!

public IEnumerable<long> GetChildren(List<Channel> list, long id) 
{ 
    foreach(Channel c in list) 
    if(c.parentID == id) 
     yield return c.ID; 
} 

IEnumerable<long>を返すのではなく必要のないコードの呼び出し中にList<long>は、new List<long>(GetChildren(list, id))またはGetChildren(list, id).ToList()のいずれかを使用することができます必要なコードを呼び出すなど、その後List<long>を返すように、これをビルドします。すべての子どもたちが取得するために

+0

はい、私の答えは働いています(私は試しました)が、あなたの解決策は本当に素晴らしいです! :) – Marco

+0

これは直ちに子供を得ることはありませんか?深いネストされたものはどうですか? – Dementic

+0

@Dementic:深くネストされたものについては私の更新を見てください。 – Oliver

関連する問題