I have a List<T>
that is a mixture of 'regular' elements and 'marker' elements. I want to split it into a List<List<T>>
, broken at the markers.
e.g. given an input of {a, b, M, c, d, e, f, M, g}, where 'M' is a marker element, I want to get {{a, b},{c, d, e, f}, {g}}
It is easy to do th开发者_如何学Goe job with a loop, but it seems like it should be possible to express it more compactly with LINQ, and after a bit of head-scratching I just can't see how. (Not enough LINQ-fu, I guess.)
Hmm...
varSplitList = myList.Aggregate(new List<List<T>>{new List<T>()},
(sl,t)=> {
if(/*T is a marker element*/) sl.Add(new List<T>());
else sl.Last().Add(t);
return sl;
});
There's probably a more readable way, but that should work. Aggregate is a very powerful "series calculation" method that is useful for these kinds of things. You provide it a "seed" (in this case a new List of Lists with a single child List), and for each element in the source, perform the operation, which takes the seed and the current element and returns the (probably modified) seed, which is then passed to the operation for the next element.
As a simpler example, here's a Factorial calculator in Linq:
long fact = Enumerable.Range(1,n).Aggregate(1, (f,n)=>f*n));
Enumerable.Range() produces an integer range from 1 to n. the Aggregate function starts with 1, and for each element, multiplies the seed by the element:
1*1=1 1*2=2 2*3=6 6*4=24 24*5=120...
At the end, fact
is given the value of the seed after all calculations are performed.
Doesn't seem like a job for LINQ. But if I were to write it without writing "a loop in LINQ clothing", I would do something like this:
var list = new List<char> {'a', 'b', 'M', 'c', 'd', 'e', 'f', 'M', 'g'};
const char marker = 'M';
var markerIndexes = list.Select((c, i) => new { c, i }).Where(z => z.c == marker).Select(z => z.i);
var split = from z in list.Select((c, i) => new { c, i })
where z.c != marker
group z.c by markerIndexes.Count(mi => z.i > mi) into g
select g.ToList();
return split.ToList();
精彩评论