2008-10-25 4 views
1

J'ai un IList de type Breadcrumb qui est juste une classe légère qui a les propriétés NavigationTitle, NavigationUrl et IsCurrent. Il est mis en cache sur le serveur Web. J'ai une méthode qui construit le fil d'Ariane actuel jusqu'à ce que le premier Fil d'Ariane dont IsCurrent a la valeur true ... utilise le code ci-dessous. C'est très laide et certainement une solution rapide, mais j'étais curieux, cela peut-il être facilement refactorisé en LINQ?Est-ce que cela peut être refactorisé en nice sympa LINQ?

IList<Breadcrumb> crumbs = new List<Breadcrumb>(); 
bool foundCurrent = false; 
for (int a = 0; a < cachedCrumbs.Count; a++) 
{ 
    crumbs.Add(crumbs[a]); 
    if (foundCurrent) 
    { 
     break; 
    } 
    foundCurrent = (crumbs[a + 1] != null && ((Breadcrumb)crumbs[a + 1]).IsCurrent); 
} 

Répondre

5

je tape ce que je pense, pour qu'il montre un train de la pensée ainsi que juste une réponse.

  • Votre source est juste cachedCrumbs
  • Vous voulez ajouter la première miette qui ne ont mis IsCurrent, mais rien après
  • TakeWhile sonne comme la voie à suivre, mais obtenir la « valeur précédente avait IsCurrent "est un peu pénible
  • Nous pouvons utiliser une fermeture pour garder efficacement une variable pour déterminer si la dernière valeur avait IsCurrent
  • Nous pouvons faire un select" no-op "pour séparer le TakeWhile du travailler sur que ce soit pour continuer

Ainsi, nous nous retrouvons avec:

bool foundCurrent = false; 

var crumbs = cachedCrumbs.TakeWhile(crumb => !foundCurrent) 
         .Select(crumb => { 
           foundCurrent = crumb == null || !crumb.IsCurrent; 
           return crumb; }); 

Je ne l'ai pas essayé, mais je pense il devrait fonctionner ... il pourrait y avoir un moyen plus simple cependant.

EDIT: Je dirais qu'en fait une boucle foreach droite est plus simple dans ce cas. Cela dit, vous pouvez écrire une autre méthode d'extension qui a agi comme TakeWhile, sauf a également retourné l'élément qui a causé l'échec de la condition. Ensuite, il serait aussi simple que:

var crumbs = cachedCrumbs.NewMethod(crumb => crumb == null || !crumb.IsCurrent); 

(je ne peux pas penser à un nom décent pour la méthode au moment où NewMethod!)

+0

Salut Jon, cela a fonctionné! Je vous remercie. Je comprends ce que fait le select, mais je suis un peu perdu sur le .TakeWhile (crumb => foundCurrent) - que fait-il exactement? Et, pourquoi voyons-nous à nouveau foundCurrent dans le select? – EvilSyn

+0

Le TakeWhile était réellement faux - il aurait dû être "! FoundCurrent" plutôt que "foundCurrent" - je l'ai corrigé. Le TakeWhile lambda est juste "continuez alors que nous n'avons pas trouvé de courant". Le lambda select consiste essentiellement à définir foundCurrent * après * le TakeWhile. –

1

Tout d'abord, ce code ne fonctionne pas. Je vais deviner que certains de ces endroits où tu utilisais des "miettes" tu voulais dire "cachedCrumbs". Dans ce cas, le code peut être réduite à:

IList<Breadcrumb> crumbs = new List<Breadcrumb>(); 
for (int a = 0; a < cachedCrumbs.Count; a++) 
{ 
    crumbs.Add(cachedCrumbs[a]); 
    if (cachedCrumbs[a] != null && cachedCrumbs[a].IsCurrent) 
    { 
      break; 
    } 
} 
+0

Bonjour James. Le code fonctionne, j'ai juste essayé de donner un aperçu de haut niveau. Je ne voulais pas dire des miettes où j'ai cachéCrumbs. Le cachedCrumbs est TOUTES les miettes possibles, dans l'ordre. Pensez-y comme des étapes dans un flux de travail, et non des fils d'Ariane réels. Merci pour le retrait sur foundCurrent though! – EvilSyn

+0

Non, vous avez mal interprété le commentaire de James - votre code avait: "crumbs.Add (miettes [a]);" où vous vouliez dire "crumbs.Add (cachedCrumbs [a])"; –

1

Une réponse alternative basée sur James Curran de - cela peut certainement être améliorée en utilisant une instruction foreach:

IList<Breadcrumb> crumbs = new List<BreadCrumb>(); 
foreach (Breadcrumb crumb in cachedCrumbs) 
{ 
    crumbs.Add(crumb); 
    if (crumb != null && crumb.IsCurrent) 
    { 
     break; 
    } 
} 
+0

foreach loop ne garantit pas l'accès aux miettes dans l'ordre où elles apparaissent réellement. – chakrit

+0

Ils le font pour toutes les collections sensibles qui prennent en charge l'indexation par numéro. Pouvez-vous penser à une implémentation IList commune qui se comporte mal à cet égard? –

+0

Thx pour le foreach .. Je l'avais d'abord, mais pour une raison quelconque l'a enlevé. :) et c'est le gâchis qui a été laissé après. :) – EvilSyn

1

Que diriez-vous ...

// find the current item 
var currentItem = cachedCrumbs.First(c => c.IsCurrent); 
var currentIdx = cachedCrumbs.IndexOf(currentItem); 

// get all items upto current item 
var crumbs = cachedCrumbs.Take(currentIdx + 2); 

a Vous pouvez transformer ceci en une méthode TakeUpto qui prend tous les éléments jusqu'à celui qui correspond aux prédicats que vous fournissez.

Que diriez-vous:

public static IEnumerable<T> TakeUpto<T>(this IList<T> theList, Func<T, bool> predicate) 
{ 
    var targetItem = theList.First(predicate); 
    var targetIdx = theList.IndexOf(targetItem); 

    return theList.Take(targetIdx + 2); 
} 

Ensuite, vous pouvez l'utiliser de cette façon:

var crumbs = cachedCrumbs.TakeUpto(c => c.IsCurrent); 

beaucoup plus propre!

Ne pas vérifier les cas nulls et off-by-one et les différences IList/IEnumerable, mais vous devriez avoir l'idée.

+0

Oh sympa! J'aime cette solution aussi! Je vous remercie. :) – EvilSyn

+0

Chakrit, j'ai beaucoup aimé cette solution et j'ai décidé de l'utiliser. Je vous remercie. :) J'aime la candidature de réutilisation de celui-ci. – EvilSyn

+0

La solution de Jon peut être refactorisée en une méthode d'extension qui est aussi réutilisable ... mais c'est un peu compliqué ... J'aime plus la lisibilité :-) mais la solution de Jon pourrait s'exécuter plus rapidement si – chakrit

1

Cette réponse est une implémentation alternative de TakeUpTo de chakrit:

public static IEnumerable<T> TakeUpto<T>(this IEnumerable<T> theList, Func<T, bool> predicate) 
{ 
    foreach (T element in theList) 
    { 
     yield return element; 
     if (predicate(element)) 
     { 
      break; 
     } 
    } 
} 

Cela ne parcourt la liste une fois, ce qui pourrait être utile dans plusieurs cas. (Supposons que la séquence en amont est le résultat d'une clause OrderBy - vous ne voulez vraiment pas qu'elle doive trier les résultats plusieurs fois sans raison valable.)

Elle permet également d'utiliser IEnumerable<T> comme source, ce qui la rend plus flexible.

Une des choses merveilleuses à propos de LINQ est la multiplicité des façons d'atteindre le même objectif.

+0

Merci Jon, j'aime vraiment cette solution aussi! – EvilSyn

Questions connexes