Linq method for checking that no elements in a collection match any other element
I take a collection of line segments and crop any match and I should get a collection that does not overlap as output.
To test my result, I want to iterate over each item in the collection and make sure it doesn't overlap with any other item.
I currently 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. It looks like this will iterate through the collection at least three times to complete the test.
Is there a better way to express this test?
source to share
Well, the merger proposal Where
, and Any
it 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 more concise ones:
if (compiledAssays.Any(a => compiledAssays.Any(b => a != b && a.Overlaps(b))))
{
throw new ApplicationException("Something went wrong."");
}
Otherwise, if your main concern is to minimize the number of loops that run, I wouldn't use Linq. I would do this instead (assuming compiledAssays
is an array, adjust if 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: Very local commentary by Raymond Chen.
My last option assumes the function is Overlaps
symmetric.
In other words, a.Overlaps(b)
it will always return the same value as b.Overlaps(a)
. If it is not, then my last option is incorrect.
source to share
Your first suggestion Where
eliminates exact duplicates (depending on what is !=
defined for assay
), so you can use compiledAssays.Distinct
as a base enum, but if you can use sort to optimize your overlap search anyway, but at the cost of an extra loop, the now accepted answer is an explicit double the loop is fine, except that it does not currently eliminate 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 reiterate the OP, again assuming that Overlaps
is symmetric.
source to share