Could this be reorganized into a pretty good LINQ?

I have an IList of type Breadcrumb, which is just a lightweight class that has the properties NavigationTitle, NavigationUrl and IsCurrent. It is cached on a web server. I have a method that builds the current path for crackers up to the first Breadcrumb, which IsCurrent is set to true ... using the code below. This is a very ugly and definitely quick trash solution, but I was curious if it can be easily converted to 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); } 
+1
source share
5 answers

I print this, I think, so that it shows the train of thought, as well as just the answer.

  • Your source is cachedCrumbs only
  • You want to add the first crumb that has IsCurrent but nothing after
  • TakeWhile sounds like a way to go, but getting "IsCurrent has a previous meaning" is a bit of a pain.
  • We can use closure to effectively save a variable that determines if the last value was set by IsCurrent
  • We can do a few "no-op" so as not to distract TakeWhile from development, whether to continue

So, in the end we get:

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

I have not tried this, but I think it should work ... maybe easier.

EDIT: I would say that actually the direct foreach loop is simpler in this case. Having said that, you can write another extension method that acted like TakeWhile, except that it also returned the element that caused the condition to fail. Then it will be as simple as:

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

(I cannot come up with a decent name for the method at the moment, hence NewMethod !)

+5
source

First of all, this code does not work. I'm going to guess that in some places where you used crumbs, you meant cachedCrumbs. If so, the code can be reduced to:

 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; } } 
+1
source

An alternative answer based on James Carran - this can certainly be improved with the foreach statement:

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

What about...

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

And you can turn this into a TakeUpto method that takes all the elements up to one that matches the given predicates.

What about:

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

Then you can use it as follows:

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

Significantly cleaner!

Do not check for null values ​​one by one, as well as IList / IEnumerable differences, but you should get this idea.

+1
source

This answer is an alternative implementation of chakrit TakeUpTo:

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

This is repeated only through the list once, which may be relevant in different cases. (Suppose the ascending sequence is the result of an OrderBy clause β€” you really don't want it to sort the results several times for no good reason.)

It also allows you to use any IEnumerable<T> as a source, which makes it more flexible.

One of the great things about LINQ is that there are several ways to achieve the same goal.

+1
source

All Articles