I've been teaching myself LINQ recently and applying it to various little puzzles. However, one of the problems I have run into is that LINQ-to-objects only works on generic collections. Is there a secret trick/ best practice for converting a non-generic collection to a generic collection?
My current implementation copies the non-generic collection to an array then operates on that, but I was wondering if there was a better way?
public static int maxSequence(string str)
{
MatchCollection matches = Regex.Matches(str, "H+|T+");
Match[] matchArr = new Match[matches.Count];
matches.CopyTo(matchArr, 0);
return matchArr
.Select(match => match.Value.Length)
.OrderByDescending(len => len)
.First();
}
A Generic collection is a class that provides type safety without having to derive from a base collection type and implement type-specific members. A Non-generic collection is a specialized class for data storage and retrieval that provides support for stacks, queues, lists and hash tables.
Code reusability: Generics help in reusing the code already written, thereby making it usable for other types (for a method, or class, or an interface).
cmd/compile: generic functions are significantly slower than identical non-generic functions in some cases #50182.
Better performance. Generic collection types generally perform better for storing and manipulating value types because there is no need to box the value types. Generic delegates enable type-safe callbacks without the need to create multiple delegate classes.
The simplest way is usually the Cast
extension method:
IEnumerable<Match> strongMatches = matches.Cast<Match>();
Note that this is deferred and streams its data, so you don't have a full "collection" as such - but it's a perfectly fine data source for LINQ queries.
Cast
is automatically called if you specify a type for the range variable in a query expression:
So to convert your query completely:
public static int MaxSequence(string str)
{
return (from Match match in Regex.Matches(str, "H+|T+")
select match.Value.Length into matchLength
orderby matchLength descending
select matchLength).First();
}
or
public static int MaxSequence(string str)
{
MatchCollection matches = Regex.Matches(str, "H+|T+");
return matches.Cast<Match>()
.Select(match => match.Value.Length)
.OrderByDescending(len => len)
.First();
}
In fact, you don't need to call OrderByDescending
and then First
here - you just want the maximal value, which the Max
method gets you. Even better, it lets you specify a projection from a source element type to the value you're trying to find, so you can do without the Select
too:
public static int MaxSequence(string str)
{
MatchCollection matches = Regex.Matches(str, "H+|T+");
return matches.Cast<Match>()
.Max(match => match.Value.Length);
}
If you have a collection which has some elements of the right type but some which may not be, you can use OfType
instead. Cast
throws an exception when it encounters an item of the "wrong" type; OfType
just skips over it.
You can use Cast
or OfType
on an IEnumerable
to convert. Cast
will throw illegal cast if the element cannot be cast to the stated type while OfType
will skip any elements that cannot be converted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With