Come convalidare se un elemento genitore ha figli nella funzione LINQ ricorsiva?

Sto facendo una funzione LINQ ricorsiva come descritto nella domanda: Simulazione della ricorsione della CTE in C #

Il mio codice è il seguente:

private static IEnumerable<KeyValuePair> getNet(List list, int? leader, int level) { return list .Where(x => x.Field("LeaderID") == leader) .SelectMany(x => new[] { new KeyValuePair(x.Field("RepID"), level) }.Concat(getNet(list, x.Field("RepID"), level+ 1)) ); } 

Mi piacerebbe convalidare se un genitore ha dei figli prima di rientrare nella funzione perché ogni bambino viene valutato di nuovo e questo consuma molto tempo.

Ad esempio, il genitore A ha 5000 figli, ma solo 5 di loro hanno figli, ho bisogno di qualcosa per convalidare se i figli di A hanno figli prima di eseguire la funzione per tutti loro.

Grazie!

Quindi, testare prima i bambini non ti aiuterà. Stai ancora facendo lo stesso lavoro, concettualmente. Se ogni chiamata ricorsiva gestisce due profondità contemporaneamente, invece di una, complichi notevolmente il metodo stesso, duplicando il lavoro della seconda profondità ogni volta che ci sono bambini, e ottenendo molto, molto poco anche quando non ci sono bambini. La parte costosa del metodo è nella ricerca lineare attraverso l’elenco per i bambini, non nella chiamata al metodo che avvia la ricerca.

Per migliorare le prestazioni devi smettere di fare ricerche lineari attraverso una lista molto ampia molte, molte volte. Fortunatamente, questo è abbastanza facile da fare. Basta creare una ricerca una volta, all’inizio del metodo, di tutti i bambini per ciascun genitore.

 var lookup = list.ToLookup(x => x.Field("LeaderID")); 

Ora, quello che stai cercando di fare, concettualmente, è attraversare un albero. Possiamo iniziare con un metodo “trasversale” generalizzato in grado di attraversare qualsiasi albero (usando un’implementazione non ricorsiva, per miglioramenti delle prestazioni):

 public static IEnumerable Traverse(IEnumerable source, Func> childSelector) { var queue = new Queue(source); while (queue.Any()) { var item = queue.Dequeue(); yield return item; foreach (var child in childSelector(item)) { queue.Enqueue(child); } } } 

Ora che abbiamo questi possiamo usare la ricerca per ottenere in modo efficiente tutti i bambini per ogni nodo, migliorando notevolmente l’implementazione. Certo, sarebbe più bello se non avessi bisogno anche della profondità di ogni nodo, ma non è ancora così male.

 var roots = lookup[leader] .Select(row => Tuple.Create(row.Field("RepID"), 0)); return Traverse(roots, node => lookup[node.Item1] .Select(row => Tuple.Create(row.Field("RepID"), node.Item2 + 1))); 

Se non hai bisogno di conoscere la profondità di ogni nodo, puoi semplificare tutto ciò fino a questo:

 var lookup = list.ToLookup( row => row.Field("LeaderID"), row => row.Field("RepID")); return Traverse(lookup[leader], node => lookup[node]);