Find the least amount of intervals which cover highest range - c#

I would like to ask for a hint how to approach in solving this task:
I have intervals ordered by min. value eg: ([1,5],[2,9],[6,7],[8,16],[11,15], [18,20]).
I should pick minimal amount of intervals, which cover the largest range.
So I should store these: ([1,5],[2,9],[8,16],[18,20]). Interval [6,7] is not stored, because it is covered by interval [2,9]. Interval [11,15] is not stored, because it is covered by [8,16].
How should I approach in solving this? Thank you:)

Linq approach
int[][] input = new[] { new[] { 1, 5 }, new[] { 2, 9 }, new[] { 6, 7 }, new[] { 8, 16 }, new[] { 11, 15 }, new[] { 18, 20 } };
int[][] result = input.Where((i1, x1) => !input.Where((i2, x2) => x1 != x2 && i2[0] <= i1[0] && i2[1] >= i1[1]).Any()).ToArray();
https://dotnetfiddle.net/x1xosT
Update: Thanks to #yuriy-faktorovich I've a added a <= and >= comparison instead < and >. Also removed the comparison with itselve.

It looks like all you need to do is keep track of the current highest range and only store numbers that are higher.
So the first highest number would be 5 so you store it, then add 9 and make it the highest number, then you reach 7 which is less than 9, so it's not stored, then you add 16 and make it the highest, 15 is less than 16, so skip it, and then finally add 20.
idk what language you're using, but the code would look something like.
foreach(var item in dataset)
{
if(item[1] > highest)
{
savedItems.Add(item);
highest = item[1];
}
}

I believe there could be local maximums, so I wrote the recursive function to look at different options
static List<(int lower,int higher)> Minimize(List<(int lower, int higher)> list, int i = 0)
{
//past last item
if (i >= list.Count) return list;
if (( i > 0 && list[i - 1].higher >= list[i].higher) ||
(i + 1 < list.Count && list[i + 1].lower == list[i].lower) ||
(i > 0 && i + 1 < list.Count && list[i + 1].lower - list[i - 1].higher <= 1))
{
var newList = list.ToList();
newList.RemoveAt(i);
var minimizedNewList = Minimize(newList);
var minimizedCurrentList = Minimize(list, i + 1);
return minimizedNewList.Count < minimizedCurrentList.Count ? minimizedNewList : minimizedCurrentList;
}
else return Minimize(list, i + 1);
}
You can test with the following
var testData = new[] {(1, 5), (2, 9), (6,7), (8, 16), (11, 15), (18, 20)};
var result = Minimize(testData.ToList());
foreach (var valueTuple in result)
{
WriteLine(valueTuple);
}
WriteLine("----------------------------");
testData = new[] { (1, 5), (5, 6), (6, 7) };
result = Minimize(testData.ToList());
foreach (var valueTuple in result)
{
WriteLine(valueTuple);
}
WriteLine("----------------------------");
testData = new[] { (1, 10), (5, 6) };
result = Minimize(testData.ToList());
foreach (var valueTuple in result)
{
WriteLine(valueTuple);
}
WriteLine("----------------------------");
testData = new[] { (1, 10), (1, 20) };
result = Minimize(testData.ToList());
foreach (var valueTuple in result)
{
WriteLine(valueTuple);
}
Here's a fiddle https://dotnetfiddle.net/EC5BrW

Related

Find, in a list, all arrays whose first element is the same as the ones that match a condition

This is a question that was very hard to title.
In C#, I have a set of int[,] arrays stored in a List<int[,]> paths, each holding a set of coordenates to form a path.
An example would be { {0,0}, {0,1}, {1,1}, {2,1} }.
Now, I want to keep in paths all paths that have the same first index as one of the first indexes of all paths that match a condition.
To better demonstrate what I mean, say I get all the paths that are of an odd length:
paths.Where(x => x.GetLength(0) % 2 == 1).ToList();
Say this returns a list containing some arrays whose first coords are either {0,0} or {0,1}, for example. I want paths to be paths.Where( x=> x's first coords are either {0,0} or {0,1}). How can i achieve this?
Hopefully, what I mean is understandable.
If you're set on using your current data structure, then you can do this, but the syntax isn't going to be pretty. It's basically like what A. Milto suggested in his answer, except that you need bounds checking to avoid throwing an exception in the case of an empty path. So if you define your paths like so:
var arrayPaths = new List<int[,]>();
arrayPaths.Add(new[,] { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 2, 1 } }); // Include: starts with (0, 0)
arrayPaths.Add(new[,] { { 0, 1 }, { 0, 1 }, { 1, 1 }, { 2, 1 } }); // Include: starts with (0, 1)
arrayPaths.Add(new[,] { { 1, 0 }, { 0, 1 }, { 1, 1 }, { 2, 1 } }); // Exclude: starts with (1, 0)
arrayPaths.Add(new int[0,0]); // Exclude: has no data
Then the subset of paths that start at (0, 0) or (0, 1) is:
arrayPaths.Where(p =>
p.GetUpperBound(0) >= 0 &&
p.GetUpperBound(1) >= 1 &&
(
(p[0, 0] == 0 && p[0, 1] == 0) ||
(p[0, 0] == 0 && p[0, 1] == 1)
));
Neville Nazerane had a good suggestion in his comment: using a data structure other than an array of integers to represent a point should result in code that's much more readily understandable. For instance, suppose you define a coordinate like so:
public struct Coordinate
{
public Coordinate(int x, int y)
{
X = x;
Y = y;
}
public int X { get; }
public int Y { get; }
public bool Equals(int x, int y) =>
X == x && Y == y;
}
Then you can define the set of paths given above like this:
var objectPaths = new List<List<Coordinate>>();
objectPaths.Add(new List<Coordinate> { new Coordinate(0, 0), new Coordinate(0, 1), new Coordinate(1, 1), new Coordinate(2, 1) });
objectPaths.Add(new List<Coordinate> { new Coordinate(0, 1), new Coordinate(0, 1), new Coordinate(1, 1), new Coordinate(2, 1) });
objectPaths.Add(new List<Coordinate> { new Coordinate(1, 0), new Coordinate(0, 1), new Coordinate(1, 1), new Coordinate(2, 1) });
objectPaths.Add(new List<Coordinate>());
And now the subset of paths you're interested in is:
objectPaths.Where(p => p.Count > 0 && (p[0].Equals(0, 0) || p[0].Equals(0, 1)));
If you want a more concise syntax for specifying paths in code, then you might consider a very simple class to represent a path. For instance:
public class Path : List<Coordinate>
{
public Path() { }
public Path(params (int x, int y)[] coordinates) =>
AddRange(coordinates.Select(c => new Coordinate(c.x, c.y)));
}
Now you can define the set of paths as:
var paths = new List<Path>();
paths.Add(new Path((0, 0), (0, 1), (1, 1), (2, 1)));
paths.Add(new Path((0, 1), (0, 1), (1, 1), (2, 1)));
paths.Add(new Path((1, 0), (0, 1), (1, 1), (2, 1)));
paths.Add(new Path());
And the syntax for selecting the subset you wanted is the same as before.
Like this:
var filteredPaths = paths.Where(x => x[0, 0] == 0 && x[0, 1] == 0 || x[0, 0] == 0 && x[0, 1] == 1);
Or if you prefer to continue working with the same collection type:
List<int[,]> filteredPaths = paths.Where(x => x[0, 0] == 0 && x[1, 0] == 0 || x[0, 0] == 0 && x[1, 0] == 1).ToList();
Here's one to go about it, assuming the List below is representing your multidimensional elements:
List<short[,]> paths = new List<short[,]>
{
new short[3, 2] { { 0, 0 }, { 4, 5 }, { 6, 7 } },
new short[3, 2] { { 0, 1 }, { 8, 9 }, { 10, 11 } },
new short[3, 2] { { 1, 1 }, { 1, 3 } ,{ 6, 1 } },
new short[3, 2] { { 2, 1 }, { 3, 5 }, { 7, 7 } }
};
define the criteria i.e:
short[,] firstCriteria = new short[1, 2] { { 0, 0 } };
short[,] secondCriteria = new short[1, 2] { { 0, 1 } };
define an extension method which should enable us to truncate the multidimensional array to only the first coordinates for filtering only.
static class Helper
{
public static IEnumerable<short> SliceRow(this short[,] array, short row)
{
for (var i = array.GetLowerBound(1); i <= array.GetUpperBound(1); i++)
{
yield return array[row, i];
}
}
}
then you can do:
var resutSet =
paths.Where(e => e.SliceRow(0).SequenceEqual(firstCriteria.Cast<short>()) ||
e.SliceRow(0).SequenceEqual(secondCriteria.Cast<short>())).ToList();
resultSet would now contain two multidimentional arrays that meet the given criteria i.e:
{ { 0, 0 }, { 4, 5 }, { 6, 7 } ,
{ 0, 1 }, { 8, 9 }, { 10, 11 } }

Converting a long nested Excel formula to c#

I am tasked to transform an Excel VBA to a web-based application. I am using Web Forms. I am stuck with a long Excel formula but I have converted half of the long formula into C#.
Here is the Excel formula:
IF(AND(D7="u", H7/F7>1), 0, INDEX(Scoring!$O$8:$O$10, SUMPRODUCT(--(E28 <= Scoring!$N8:$N10),--(E28 >= Scoring!$N8:$N10), ROW(INDIRECT("'Scoring'!$M1:$M3"))))
While here is my progress on the C# method. I need help on the ELSE part:
IF (variable = "u" && ((CurrentValue / AcceptedValue) > 1)){
return 0;
ELSE {
// the INDEX clause on the Excel Formula
}
O8 - O10 values are {2, 1, 0}
N8 - N10 values are {1, 1.5, 9999999999}
E28 is 0
I have also got the result of the remainder of the formula piece by piece. I just need guidance on how to put it in C#.
=INDEX(Scoring!$O$8:$O$10, SUMPRODUCT({1,1,1}, {0,0,0}, 1))
The double minus(--) made it return 0/1 instead of true/false. ROW(INDIRECT($M1-$M3)) is equivalent to 1.
I could use some help on transforming the Excel formula into C# code.
Here is some C# code to replicate this part of your formula:
INDEX(Scoring!$O$8:$O$10, SUMPRODUCT(--(E28 <= Scoring!$N8:$N10),--(E28 >= Scoring!$N8:$N10), ROW(INDIRECT("'Scoring'!$M1:$M3")))
In the code I've had to assign the array {1, 1, 1} for ROW(INDIRECT("'Scoring'!$M1:$M3")). There is some ambiguity in your question around the action of SUMPRODUCT. According to support.office.com
Multiplies corresponding components in the given arrays, and returns the sum of those products.
But some of your detail in the question makes me think you are thinking that each array get summed up and then those results get multiplied together. Anyway, check out the comments in the code below to follow the logic:
// inputs
double[] o8o10 = new double[] { 2, 1, 0 }; // Scoring!$O$8:$O$10
double[] n8n10 = new double[] { 1, 1.5, 9999999999 }; // Scoring!$N8:$N10
double e28 = 0; // E28
// entries to SUMPRODUCT
List<int> test1 = new List<int>();
List<int> test2 = new List<int>();
Array.ForEach(n8n10, x => { test1.Add((e28 <= x) ? 0 : 1); });
Array.ForEach(n8n10, x => { test2.Add((e28 >= x) ? 0 : 1); });
// ROW(INDIRECT("'Scoring'!$M1:$M3")) should be an array !
List<int> test3 = new List<int> { 1, 1, 1 };
// evalue SUMPRODUCT
int sumProductResult = 0;
for (var i=0; i<test1.Count; i++)
{
sumProductResult += test1[i] * test2[i] * test3[i];
}
// evalute INDEX
double indexResult = 0;
indexResult = o8o10[sumProductResult];
// output
Console.WriteLine(indexResult);
Console.ReadKey();
The output for me is 2 because that is the 0th element of the o8o10 array. We get 0 for the array index because
=SUMPRODUCT({1, 1, 1}, {0, 0, 0}, {1, 1, 1})
resolves to:
=SUM(1*0*1, 1*0*1, 1*0*1}
Which gives 0.
HTH
ROW(INDIRECT("'Scoring'!$M1:$M3")) actually evaluates to {1; 2; 3} and
--(E28 <= Scoring!$N8:$N10),--(E28 >= Scoring!$N8:$N10) is --(E28 = Scoring!$N8:$N10)
so the SUMPRODUCT formula is just:
(E28 = Scoring!$N8) * 1 + (E28 = Scoring!$N9) * 2 + (E28 = Scoring!$N10) * 3
and in C#:
double[] n8n10 = { 1, 2, 3 }; double e28 = 2;
double sumProduct = n8n10.Select((d, i) d == e28 ? i + 1 : 0).Sum();
But! because Scoring!$O$8:$O$10 is only 3 cells and the SUMPRODUCT can in theory result in more than 3, the actual aim of the INDEX SUMPRODUCT part seems to be something like this:
double[] o8o10 = { 4, 5, 6 }, n8n10 = { 1, 2, 3 };
double e28 = 2, result = o8o10[0]; // INDEX(array, 0) results in array
if (n8n10[1] == e28) result = o8o10[1];
else if (n8n10[2] == e28) result = o8o10[2];

Feedback Loop Filtering query using LINQ [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have the following scenario (based on an actual scenario that cannot be shown here). Two inputs
A number x
A series of positive integers given as IEnumerable<int>
Now I want to find all groups of three consecutive integers in the series that can divide x with no remainder, and one group cannot overlap the other.
For instance: My series is 1, 2, 3, 4, 5, 6 and user inputs x=24
Then my LINQ will give 1, 2, 3, but will not give 2, 3, 4 because those two groups will overlap in the series.
Now I could run a LINQ query that would basically run a "window" of 3 on the series and give me the several groups, and run a Where clause to find the group that will have all 3 numbers successfully dividing x. But every time I find a match, I need some kind of a negative feedback to tell my filter to omit the subsequent groups that will be overlapping to the matching one.
To understand it better, in the above example. I can generate several groups like
1, 2, 3
2, 3, 4
3, 4, 5,
and so on
then I could say
myWindowsOf3Numbers
.Where(w => x % w.Number1 == 0 && x % w.Number2 == 0 && x % w.Number3 == 0)
So I end up with code like this
var series = new[] { 1, 2, 3, 4, 5, 6, 7 };
var x = 24;
var windows = series.Select((number, index) => index + 2 < series.Count()? new { Number1 = series[index], Number2 = series[index+1], Number3 = series[index+2] } : null )
.Where(groups => groups != null);
var matching = windows.Where(w => x % w.Number1 == 0 && x % w.Number2 == 0 && x % w.Number3 == 0);
The above will give me 1, 2, 3 and 2, 3, 4 both, but I want only the first one of the two overlapping.
Is there any LINQ-trick to do that or I have to use foreach (Like the following code)?
private class Match
{
public int N1 { get; set; }
public int N2 { get; set; }
public int N3 { get; set; }
}
var series = new[] { 1, 2, 3, 4, 5, 6, 7 };
var x = 24;
var windows = series.Select((number, index) => index + 2 < series.Count() ? new { Number1 = series[index], Number2 = series[index + 1], Number3 = series[index + 2] } : null)
.Where(groups => groups != null);
var matches = new List<Match>();
for (var i = 0; i < (series.Count() - 2); i ++)
{
if (x % series[i] == 0 && x % series[i + 1] == 0 && x % series[i + 2] == 0)
{
matches.Add(new Match() { N1 = series[i], N2 = series[i + 1], N3 = series[i + 2] });
i += 3;
}
}
For the sake of the challenge, here is one possible "pure" LINQ solution, but frankly I'd never use something like this:
int x = 60;
var series = new [] { 1, 2, 3, 4, 5, 6 }.AsEnumerable();
var matches = series
// Create the sliding window
.Select((e, i) => new { index = i, group = series.Skip(i).Take(3).ToArray() })
// Remove the non matching
.Where(e => e.group.Length == 3 && e.group.All(v => (x % v) == 0))
// Remove the overlapping
.Aggregate(new { next = 0, result = Enumerable.Empty<int[]>() }, (prev, next) =>
next.index >= prev.next ?
new { next = next.index + 3, result = prev.result.Concat(new[] { next.group }) } :
prev).result;
Well, you said LINQ ...:
var series = new[] { 1, 2, 3, 4, 5, 6, 7 };
var x = 24;
var matchingWindows = series
.Select((number, index) => index + 2 < series.Length ?
new { Number1 = series[index], Number2 = series[index + 1], Number3 = series[index + 2], Index = index } :
null)
.Where(groups => groups != null)
.Where(w => x % w.Number1 == 0 && x % w.Number2 == 0 && x % w.Number3 == 0)
.ToList();
int lastTakenIndex = -1;
var nonOverlapping = matchingWindows.Where(w =>
{
if (lastTakenIndex >= 0)
{
if (w.Index <= lastTakenIndex + 2) return false;
lastTakenIndex = w.Index;
return true;
}
lastTakenIndex = w.Index;
return true;
}).ToList();
It uses a lastTakenIndex (= the start index of the 3-group that was last taken that matched the condition) which is modified as a side-effect during the Where filtering.

Find a series of the same number in a List

I have a List of items containing either 1 or 0, I'm looking to output the items only where there are six 1's back to back in the list. So only write to the console if the item in this list is part of a group of six.
1
1
1
1
1
1
0
1
1
1
0
In the above list, the first six items would be output but the bottom set of three 1s would not as they are not part of a group of six.
Is this a job for LINQ or RegEx?
You can concatenate all values into string, then split it by zeros. From substrings select those which have at least 6 characters:
List<int> values = new List<int> { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0 };
var series = String.Concat(values)
.Split(new[] { '0' }, StringSplitOptions.RemoveEmptyEntries)
.Where(s => s.Length >= 6);
For given input data series will contain single item "111111" which you can output to console.
Classic run length encoding, O(n), lazy evaluated, stack agnostic, generic for any equatable type.
public void TestRunLength()
{
var runs = new List<int>{ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 3, 4, 4, 0, 4};
var finalGroup = RunLength(runs).FirstOrDefault(i => i.Count == 6 && i.First() == 1);
}
private IEnumerable<List<T>> RunLength<T>(IEnumerable<T> source) where T : IEquatable<T>
{
T current = default(T);
var requiresInit = true;
var list = new List<T>();
foreach (var i in source)
{
if (requiresInit)
{
current = i;
requiresInit = false;
}
if (i.Equals(current))
{
list.Add(i);
}
else
{
yield return list;
list = new List<T>{ i };
current = i;
}
}
if (list.Any())
{
yield return list;
}
}
And because it's lazy it works on infinite sequences (yes I know its not infinite, but it is large)!
public void TestRunLength()
{
var random = new Random();
var runs = Enumerable.Range(int.MinValue, int.MaxValue)
.Select(i => random.Next(0, 10));
var finalGroup = RunLength(runs)
.FirstOrDefault(i => i.Count == 6);
}
Probably it can be done with Regex too if you concatenate your numbers into a string. But I would prefer linq:
var bits = new List<int> {1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0};
int bitCountPerGroup = 6;
var result = bits // (1) (2)
.Select((x,idx) => bits.Skip(idx).TakeWhile(y => y == x))
.Where(g => g.Count() == bitCountPerGroup); // (3)
foreach (var set in result)
Console.WriteLine(string.Join(" ", set));
This code gets a number-set for each number by starting from the number (1) and taking the next numbers as long as they are equal (2). Then filter the groups and gets only those groups which have 6 numbers (3).
If for example your list is of an unknown size,or better,you do not know the items in it you could do this recursive example(note that i placed more zeros so it would fetch 2 sets of data,it works with yours also),and pass to the method the amout to group by:
//this is the datastructure to hold the results
static List<KeyValuePair<string, List<int>>> Set = new List<KeyValuePair<string, List<int>>>();
private static void GetData(List<int> lst, int group)
{
int count = 1;
int pivot = lst.First();
if (lst.Count < group)
{
return;
}
else
{
foreach (int i in lst.Skip(1))
{
if (i == pivot)
{
count++;
}
else if (count == group)
{
Set.Add(new KeyValuePair<string, List<int>>("Set of items " + pivot, lst.Take(count).ToList()));
GetData(lst.Skip(count).ToList(), group);
break;
}
else
{
GetData(lst.Skip(count).ToList(), group);
break;
}
}
}
}
Then in Main():
static void Main(string[] args)
{
List<int> test = new List<int> { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0 };
GetData(test, 6);
foreach (var item in Set)
{
Console.WriteLine("\t" + item.Key);
foreach (var subitem in item.Value)
{
Console.WriteLine(subitem);
}
}
}

LINQ to find the closest number that is greater / less than an input

Suppose I have this number list:
List<int> = new List<int>(){3,5,8,11,12,13,14,21}
Suppose that I want to get the closest number that is less than 11, it would be 8
Suppose that I want to get the closest number that is greater than 13 that would be 14.
The numbers in list can't be duplicated and are always ordered. How can I write Linq for this?
with Linq assuming that the list is ordered I would do it like this:
var l = new List<int>() { 3, 5, 8, 11, 12, 13, 14, 21 };
var lessThan11 = l.TakeWhile(p => p < 11).Last();
var greaterThan13 = l.SkipWhile(p => p <= 13).First();
EDIT:
As I have received negative feedback about this answer and for the sake of people that may see this answer and while it's accepted don't go further, I explored the other comments regarding BinarySearch and decided to add the second option in here (with some minor change).
This is the not sufficient way presented somewhere else:
var l = new List<int>() { 3, 5, 8, 11, 12, 13, 14, 21 };
var indexLessThan11 = ~l.BinarySearch(10) -1;
var value = l[indexLessThan11];
Now the code above doesn't cope with the fact that the value 10 might actually be in the list (in which case one shouldn't invert the index)! so the good way is to do it:
var l = new List<int>() { 3, 5, 8, 11, 12, 13, 14, 21 };
var indexLessThan11 = l.BinarySearch(10);
if (indexLessThan11 < 0) // the value 10 wasn't found
{
indexLessThan11 = ~indexLessThan11;
indexLessThan11 -= 1;
}
var value = l[indexLessThan11];
I simply want to note that:
l.BinarySearch(11) == 3
//and
l.BinarySearch(10) == -4;
Use Array.BinarySearch - no need for LINQ or visiting on average half the elements to find your target.
There are also a variety of SortedXXX classes that may be suitable for what you're doing [that will have such efficient O(log N) searches built-in]
You can do this using a binary search. If your searching for 11, well obviously you'll get the index your after. If you search for 10 and use the bitwise complement of the result, you'll get the closest match.
List<int> list = new List<int>(){3,5,8,11,12,13,14,21};
list.Sort();
int index = list.BinarySearch(10);
int found = (~index)-1;
Console.WriteLine (list[found]); // Outputs 8
The same goes searching in the other direction
int index = list.BinarySearch(15);
Console.WriteLine("Closest match : " + list[+~index]); // Outputs 21
Binary searches are also extremely fast.
closest number below 11:
int someNumber = 11;
List<int> list = new List<int> { 3, 5, 8, 11, 12, 13, 14, 21 };
var intermediate = from i in list
where i < someNumber
orderby i descending
select i;
var result = intermediate.FirstOrDefault();
closest number above 13:
int someNumber = 13;
List<int> list = new List<int> { 3, 5, 8, 11, 12, 13, 14, 21 };
var intermediate = from i in list
where i > someNumber
orderby i
select i;
var result = intermediate.FirstOrDefault();
This is my answer
List<int> myList = new List<int>() { 3, 5, 8, 11, 12, 13, 14, 21 };
int n = 11;
int? smallerNumberCloseToInput = (from n1 in myList
where n1 < n
orderby n1 descending
select n1).First();
int? largerNumberCloseToInput = (from n1 in myList
where n1 > n
orderby n1 ascending
select n1).First();
var list = new List<int> {14,2,13,11,5,8,21,12,3};
var tested = 11;
var closestGreater = list.OrderBy(n => n)
.FirstOrDefault(n => tested < n); // = 12
var closestLess = list.OrderByDescending(n => n)
.FirstOrDefault(n => tested > n); // = 8
if (closestGreater == 0)
System.Diagnostics.Debug.WriteLine(
string.Format("No number greater then {0} exists in the list", tested));
if (closestLess == 0)
System.Diagnostics.Debug.WriteLine(
string.Format("No number smaler then {0} exists in the list", tested));
Here is my way hope this helps somebody!
List<float> list = new List<float> { 4.0f, 5.0f, 6.0f, 10.0f, 4.5f, 4.0f, 5.0f, 6.0f, 10.0f, 4.5f, 4.0f, 5.0f, 6.0f, 10.0f };
float num = 4.7f;
float closestAbove = list.Aggregate((x , y) => (x < num ? y : y < num ? x : (Math.Abs(x - num)) < Math.Abs(y - num) ? x : y));
float closestBelow = list.Aggregate((x , y) => (x > num ? y : y > num ? x : (Math.Abs(x - num)) < Math.Abs(y - num) ? x : y));
Console.WriteLine(closestAbove);
Console.WriteLine(closestBelow);
This means you dont have to order the list
Credit: addapted from here: How to get the closest number from a List<int> with LINQ?
The Expanded Code
float closestAboveExplained = list.Aggregate((closestAbove , next) => {
if(next < num){
return closestAbove;
}
if(closestAbove < num){
return next;
}
else{
if(Math.Abs(closestAbove - num) < Math.Abs(next - num)){
return closestAbove;
}
}
return next;
});
You can use a query for this such as:
List<int> numbers = new List<int>() { 3, 5, 8, 11, 12, 13, 14, 21 };
List<int> output = (from n in numbers
where n > 13 // or whatever
orderby n ascending //or descending
select n).ToList();

Categories

Resources