Reputation: 3188
LINQ is like voodo magic to me. No need to show you what I've done, nothing works or even compiles, I'm just getting error in a somewhat not understandable language:
The type arguments for method 'System.Linq.Enumerable.SelectMany<TSource,TResult>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,int,System.Collections.Generic.IEnumerable<TResult>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
So, here's my code:
foreach (string first in firstStrings)
foreach (string second in secondStrings)
{
try {
if (second.Contains(DoFormatting(first)))
DoStuff(first, second);
}
catch (Exception e)
{
LogStuff(first, second);
}
}
(How) can I translate it to Linq?
Upvotes: 0
Views: 1196
Reputation: 4434
If you really want to you could create an extension method as below:
public static class ExtensionMethods
{
public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach (T item in enumeration)
{
action(item);
}
}
}
and then use LINQ as follows:
firstStrings.ForEach(first => secondStrings.ForEach(second =>
{
try
{
if (second.Contains(DoFormatting(first)))
{
DoStuff(first, second);
}
}
catch(Exception e)
{
LogStuff(first, second);
}
}));
But, as other commentors have mentioned, I would keep your code as is (at least in regard to whether you convert it to LINQ).
Upvotes: 2
Reputation: 1567
The issue here is that LINQ was designed using principles of functional programming, and the big principle there is to not cause side effects.
That said there are some things you can do, using a combination of juharr and Erik's approaches this would work:
var ContainedPairs=
firstStrings.Join(secondStrings,
x => true,
y => true,
(first, second) => new { first, second })
.Where(pairs => pairs.second.Contains(DoFormatting(pairs.first)));
//loop over pairs calling dostuff and error handling
Note that the important thing here is that DoFormatting must be guaranteed not to change pairs.first, and not to throw an error.
If this is not the case then changing the signature of DoFormatting to the following:
private bool ContainsFormatted(string first, string second, out Exception ex)
would allow you to do this:
var pairResults =
firstStrings.Join(secondStrings,
x => true,
y => true,
(first, second) => new { first, second })
.Select(pair =>
{
Exception ex = null;
var containsFormatted = ContainsFormatted(pair.second, pair.first, out ex);
return new { pair, containsFormatted, ex };
});
//loop over pair results, calling DoStuff on items where containsFormatted = true and logging exceptions etc otherwise
The point here is that side effects and error handling are still left out of linq, but that doesn't mean that you need to give up the benefits of a more functional style of programming
Upvotes: 1
Reputation: 93444
You can't really translate much of it to Linq because you still have to iterate over all of the permutation, which requires two separate nested loops.
You could do this, it doesn't save a lot, but some:
foreach (string first in firstStrings)
try {
foreach (var second in secondStrings.Where(second => second.Contains(DoFormatting(first))))
DoStuff(first, second);
}
catch (Exception e) {
LogStuff(first, second);
}
EDIT:
Note that the logging here would have to change because second is not available in the catch block, which is a problem with moving things to linq.. you have less access to which item in the collection caused the error. You would probably have to move the exception handling into both DoFormtting and DoStuff.
And, as juharr pointed out, if an exception is thrown it will terminate processing of secondStrings rather than continuing to the next. So that may not be the desired function either.. again, a side effect of moving to linq, you lose the ability to do fine grained exception handling and continuation.
Upvotes: 1
Reputation: 32296
You could do the following to reduce it down to one foreach
, but honestly you might as well leave it as is.
var pairs = from first in firstStrings
from second in secondStrings
select new
{
first,
second
};
foreach(var pair in pairs)
{
try
{
if (pair.second.Contains(DoFormatting(pair.first)))
DoStuff(pair.first, pair.second);
}
catch (Exception e)
{
LogStuff(pair.first, pair.second);
}
}
OR with extension methods
var pairs = firstStrings.Join(
secondStrings,
x=>true,
y=>true,
(first, second) => new { first, second});
foreach(var pair in pairs)
{
try
{
if (pair.second.Contains(DoFormatting(pair.first)))
DoStuff(pair.first, pair.second);
}
catch (Exception e)
{
LogStuff(pair.first, pair.second);
}
}
I would not use a ForEach
extension method because enumerating over an enumeration and creating side effects is against the best practices of using Linq.
Upvotes: 2
Reputation: 129
I would recommend taking a look at Lambda expressions.
http://msdn.microsoft.com/en-us/library/bb397675.aspx
So, the LINQ translation would look something like this
String result = second.Where( r => r.Contains(first));
Upvotes: 0