Reputation: 8928
I'm taking a collection of line segments and trimming any that overlap and I should get a collection that does not overlap as output.
To test my output I want to iterate over each item in the collection and ensure that it does not overlap with any other item.
Currently I have this code:
foreach (var assay in compiledAssays)
{
if (compiledAssays.Where(a => a != assay).Any(a => a.Overlaps(assay)))
{
throw new ApplicationException("Something went wrong.");
}
}
It's readable but it "smells bad" to me. Seems like it would be iterating through the collection at least three times to do the test.
Is there a better way of expressing this test?
Upvotes: 3
Views: 244
Reputation: 10931
Your first Where
clause is excluding exact duplicates (depending upon what !=
is defined to be for assay
), as such you can use compiledAssays.Distinct
as the base enumeration, but given you could use a sort to optimise the search for overlaps anyway, but at the expense of an extra loop, the now accepted answer's explicit double loop is fine, except it doesn't currently exclude all duplicates. It should be:
for (int i = 0; i < compiledAssays.Length; i++)
{
for (int j = i + 1; j < compiledAssays.Length; j++)
{
if (compiledAssays[i] != compiledAssays[j]
&& compiledAssays[i].Overlaps(compiledAssays[j]))
{
throw new ApplicationException("Something went wrong.");
}
}
}
to replicate the OP, again assuming Overlaps
is symmetric.
Upvotes: 1
Reputation: 36483
Well, merging the Where
and the Any
clauses would be a good first improvement:
foreach (var assay in compiledAssays)
{
if (compiledAssays.Any(a => a != assay && a.Overlaps(assay)))
{
throw new ApplicationException("Something went wrong.");
}
}
You can also try the more succinct:
if (compiledAssays.Any(a => compiledAssays.Any(b => a != b && a.Overlaps(b))))
{
throw new ApplicationException("Something went wrong."");
}
Otherwise, if your primary concern is to minimize the amount of loops that are performed, I wouldn't use Linq. I would do this instead (assumes compiledAssays
is an array, adjust as necessary):
for (int i = 0; i < compiledAssays.Length; i++)
{
for (int j = i + 1; j < compiledAssays.Length; j++)
{
if (compiledAssays[i].Overlaps(compiledAssays[j]))
{
throw new ApplicationException("Something went wrong.");
}
}
}
EDIT: A very pertinent comment by Raymond Chen.
My last option assumes that the Overlaps
function is symmetric.
In other words, that a.Overlaps(b)
will always return the same value as b.Overlaps(a)
. If this is not the case, then my last option is incorrect.
Upvotes: 3