1

I have a list of numbers, I would like to get the next >= number than the one I pass, but there's a problem when I pass value that's bigger to the biggest in list. For example when I pass 41 and the biggest in list is 40 it won't work so I'd like it to return 40.

var numbers = new[] {30, 20, 40};

I would like it to work like:

numbers.GetNearest(45) -> 40
numbers.GetNearest(40) -> 40
numbers.GetNearest(31) -> 40
numbers.GetNearest(30) -> 30
numbers.GetNearest(29) -> 30
numbers.GetNearest(1) -> 20

Is this possible with LINQ alone, or is there any well-known algorithm for this?

What I have now is something like:

numbers.OrderBy(n => n).FirstOrDefault(n => n >= minute)

but it doesn't work when I pass value bigger than 40

I don't want the closest number, but the next one >= than the one I pass, but for certain numbers bigger than the biggest in list won't work so I'd like to return just the last one.

Konrad
  • 6,385
  • 12
  • 53
  • 96
  • 2
    Your title talks about "specific conditions" but you don't describe what those are. We can *maybe* guess from the code that you've written, but as you've said that doesn't work, it's hard to say for sure. Please provide more details about what you're trying to achieve. – Jon Skeet Sep 28 '18 at 09:27
  • Possible duplicate of [How to get the closest number from a List with LINQ?](https://stackoverflow.com/questions/5953552/how-to-get-the-closest-number-from-a-listint-with-linq) – FCin Sep 28 '18 at 09:31
  • 1
    Do you want the nearest bigger number and in case it does not exist then the nearest smaller number? That is the only way your cae of 31->40 and 45 -> 40 can be explained? – peeyush singh Sep 28 '18 at 09:33
  • @peeyushsingh yeah – Konrad Sep 28 '18 at 09:41
  • What is there is no items in `numbers` array? There will be no last one number. – apocalypse Sep 28 '18 at 09:47
  • 1
    @apocalypse fair point, I can just check if it's empty first and return 0, but it depends on the case. – Konrad Sep 28 '18 at 09:50
  • why not try an extension method on int arrays? – BenKoshy Sep 28 '18 at 09:53
  • @apocalypse this quesion is not about exception handling – fubo Sep 28 '18 at 09:55

4 Answers4

5

Define that special case with DefaultIfEmpty()

var numbers = new[] { 30, 20, 40 };
Array.Sort(numbers);
int result = numbers.Where(n => n >= minute).DefaultIfEmpty(numbers.Last()).First();

Testcases: https://dotnetfiddle.net/nIEsRO

fubo
  • 44,811
  • 17
  • 103
  • 137
  • This is not pure `Linq`. – apocalypse Sep 28 '18 at 09:41
  • 1
    @apocalypse since the input is a array, I'd prefer `Array.Sort(numbers);` over `numbers = numbers.OrderBy(x => x).ToArray();` because it's faster – fubo Sep 28 '18 at 09:43
  • @fubo: Yeah I know. But what if we have array of 1,000,000 numbers? What if we don't want to modify input array? Or we don't have array but IEnumerable? – apocalypse Sep 28 '18 at 09:46
  • 2
    The answer is [DefaultIfEmpty](https://learn.microsoft.com/en-us/dotnet/api/system.linq.queryable.defaultifempty?view=netframework-4.7.2). The fact that there is an `Array.Sort` is irrevelant. – Drag and Drop Sep 28 '18 at 09:49
3

Update

var numbers = new[]{30, 20, 40};
var number = 25;
var closest = numbers.OrderBy(n => n)
                     .Cast<int?>()
                     .FirstOrDefault(n => n >= number) ?? numbers.Last();
Console.WriteLine(closest);

Note : The Cast is just to handle 0 due to default(int) maybe a better way to do this though

output

30

Full Demo Here


Original

You could just order by the difference

var closest = numbers.OrderBy(x => Math.Abs((long)x - number))
                     .First();
TheGeneral
  • 79,002
  • 9
  • 103
  • 141
1

You didn't provide enough details about specific conditions, so I can only give you this answer:

var numbers = new[] {30, 20, 40};

var ordered = numbers.Select(x => (int?)x).OrderBy(x => x);

int? value  = ordered.FirstOrDefault(x => x >= 41) ?? ordered.LastOrDefault();
apocalypse
  • 5,764
  • 9
  • 47
  • 95
0

Someone posted an answer with Aggregate but not working exactly how I wanted it to work but deleted it. So I came up with my own solution using Aggregate which should be faster than using OrderBy first I think:

numbers.Aggregate((x, y) =>
{
    var min = Math.Min(x, y);
    return number <= min ? min : Math.Max(x, y);
});

It took me a while to figure it out but it solves my problem. If you can see any flaws in it, let me know.

Konrad
  • 6,385
  • 12
  • 53
  • 96