# Find items in collection one that are the greatest value less than an item in collection two

This is hard to explain. I wanted to get all of the items in collection one (ints in the example below) that are the highest values that are lower than any of the items in collection two (otherInts). In the example below, only 2 and 8 should be returned from ints. 1 doesn't qualify because it is lower than 3 (in otherInts), but 2 is higher than 1, but still lower than 3. 2 does qualify because there is nothing higher than it that is lower than 3. 5 does not qualify for the same reason as 1, namely, 8 is higher than it, but also lower than 9 (in otherInts).

I've written the following linq to accomplish this. I'd like to stay constrained to linq as opposed to a standard forEach since this is a sub-linq statement. Is there a better linq way to accomplish this?

var ints = new List<int>{1,2,5,8};
var otherInts = new List<int>{3,4,9};

var intsImmediatelyLessThanOrEqualToOtherInts = from i in ints
let firstGreaterOtherInt = otherInts.First(o => o > i)
let intBetweenThisAndOther = from i2 in ints
where i2 > i && i2 < firstGreaterOtherInt
select i2
select intBetweenThisAndOther.Count() > 0 ? null as object : i;

intsImmediatelyLessThanOrEqualToOtherInts.Where(i => i != null).ToList().ForEach(Console.WriteLine);


Here's a working fiddle if you want to test other cases.

## 2 Answers

To me there can only be two such numbers in the first set an they can be selected with these two queries:

var set1MaxLessThenSet1Min = set1.TakeWhile(i => i < set2 .Min()).LastOrDefault();
var set1MaxLessThenSet2Min = set1.TakeWhile(i => i < set2 .Max()).LastOrDefault();


this means that for those two sets:

var set1 = new List<int> { 1, 5, 6, 7, 9 };
var set2 = new List<int> { 3, 4, 9, 10, 11 };


those numbers are 1 and 9 whereas your query finds also the 7 which is not at the same time the highest number in set1 and lower then the highest in set2.

Both queries are for ordered sets. If they are not ordered the Where clause should be used.

Alternatively:

    var ints = new List<int>{ 1, 5, 6, 7, 9 };
var otherInts = new List<int>{ 3, 4, 9, 10, 11 };

var intsImmediatelyLessThanOrEqualToOtherInts =
otherInts.
OrderBy(o => o). // (just in case otherInts isn't already sorted)
Select(o => ints.Where(i => i <= o).Max()).
Distinct();

Console.WriteLine(string.Join(", ", intsImmediatelyLessThanOrEqualToOtherInts.ToArray()));


which yields:

1, 9


(and yields "2, 8" with the same input as in your question)

'Hope this helps.