2011-10-25 5 views
-4

haloa!Récursive get ID

i ont un List<Channel>Channel est une classe personnalisée biensur:

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

strcuture peut être quelque chose comme:

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

et ainsi de suite.

ce que je voudrais faire est d'obtenir tous les enfants ID de d'un canal spécifique:

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

Et voulez-vous aussi les enfants des enfants? Veuillez ajouter un autre niveau aux données d'échantillon et lister la sortie désirée. –

+0

L'ID 28 est répété, est-ce une erreur? –

+2

Ne serait-il pas plus simple de faire fonctionner ceci si le 'Channel' avait un' List Children {get; ensemble privé; } '? – Oliver

Répondre

2

Pour tous les enfants:

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

construire ce que le retour d'un IEnumerable<long> plutôt que de retourner un code qu'appeler alors List<long> qui a besoin d'un List<long> peut utiliser new List<long>(GetChildren(list, id)) ou GetChildren(list, id).ToList(), tout en appelant le code qui n'a pas besoin cela peut obtenir de meilleures performances en mémoire et en temps de premier résultat en ne construisant pas une liste dont il n'a pas réellement besoin, comme dans foreach(long childID in GetChildren(list, id)).

Pour obtenir tous les descendants (enfants, petits-enfants, arrière-petits-enfants, etc.), qui est le seul cas que nous pourrions faire usage de récursion (selon le titre de votre question) utilisez:

En supposant qu'il ne peut y avoir des doublons (même petits-enfants à travers de multiples routes):

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

S'il peut y avoir des doublons vous pourriez alors appliquer .Distinct() à ce qui précède, ou rendez-vous pour:

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

Cela dit, je préférerais probablement modéliser cela en faisant en sorte que les classes de canaux maintiennent un List<Channel> d'enfants.

0

Ayant

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

dans votre classe, vous pouvez faire:

Pas de récursion (donc seulement des enfants)

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

ou avec récursion (enfants des enfants aussi)

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

@Dementic: jetez un oeil à mon code édité. Est-ce ce dont vous avez besoin? – Marco

+0

Avoir ret dans votre classe introduit un problème de simultanéité (deux threads seront ajoutés à la même liste) et un bug où appeler la méthode deux fois aura les résultats des deux appels, puisque ret n'est jamais effacé. Puisque ret n'a rien à voir avec la classe, mais seulement avec l'appel particulier, ce devrait être un local créé dans la méthode elle-même. –

1

Je ne sais pas si la réponse Marcos produit vraiment le résultat souhaité, mais j'écrire cela dans un plus mode 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; 
} 

Si vous également besoin des profonds imbriqués vous pourriez peut-être utiliser cette fonction:

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

Mais sachez qu'il ne vérifie pas la redondance cyclique et pourrait se retrouver dans une exception StackOverflow!

+0

Oui, ma réponse fonctionne (j'ai essayé) mais votre solution est vraiment sympa !! :) – Marco

+0

ne pas obtenir seulement les enfants immédiats? Qu'en est-il des profonds imbriqués? – Dementic

+0

@Dementic: voir ma mise à jour pour les imbriqués profonds. – Oliver