Related
given:
int[] myArray = new int[]{-8, -17, 12, 8, 16, -3, 7, 3};
Find all values in array closest to 0.
Currently using another code found on the site but it only tells me 1 value.
int nearest = myArray.Select(p => new {Value = p, Difference = Math.Abs(p - searchValue)}).OrderBy(p => p.Difference).First().Value;
In the current case both -3 and 3 are closest to 0 but since -3 comes first it only outputs -3.
Is there another way to do this where it finds all values instead of just First()?
Just to add to this. If you want to do this using O(n) complexity without sorting then you can do it this way :
public List<int> GetClosestNumbers(int searchVal)
{
int[] myArray = new int[] { -8, -17, 12, 8, 16, -3, 7, 3 };
int minimumDist = int.MaxValue;
List<int> output = new List<int>();
for (int i = 0; i < myArray.Length; i++)
{
var currentDistance = Math.Abs(myArray[i] - searchVal);
if (currentDistance < minimumDist)
{
minimumDist = currentDistance;
output.Clear();
output.Add(myArray[i]);
}
else if (minimumDist == currentDistance)
{
output.Add(myArray[i]);
}
}
return output;
}
Use a grouping on the lowest absolute value
int[] result = myArray
.OrderBy(i => Math.Abs(i - searchValue))
.GroupBy(i => Math.Abs(i - searchValue))
.First()
.ToArray();
I have a collection of integers e.g.
INPUT EXAMPLE
4,7,9,8,20,56,78,34,2,76,84,98
I need to sort this list in a way that any number til 20 will be sorted in ascending order and above 20 will be sorted in descending order. So output will be :
OUTPUT EXAMPLE
2,4,7,8,9,20,98,84,78,76,56,34
I wrote a comparer for it. but now trying for more clean approach may be by using the existing tools like orderby.
You can do that using two sort groups:
list.OrderBy(i => i <= 20 ? i : int.MaxValue) // sort numbers less than 20 ascending; put numbers greater than 20 at the end
.ThenByDescending(i => i) // sort remaining numbers descending
You can trivially do this using a custom comparer:
public class MyCustomComparer : IComparer<int>
{
private readonly int _cutOffPointInclusive;
public MyCustomComparer(int cutOffPointInclusive)
{
_cutOffPointInclusive = cutOffPointInclusive;
}
public int Compare(int x, int y)
{
if (x <= _cutOffPointInclusive || y <= _cutOffPointInclusive)
{
return x.CompareTo(y);
}
else
{
return y.CompareTo(x);
}
}
}
This sorts ascendingly when either value to compare is lower than or equal to the cutoff point (both to push the greater values to the top and to sort the values up till the cutoff point ascendingly), and descendingly when both are greater than the cutoff point (to actually sort those greater values descendingly).
Tested using:
var testData = new List<int>{ 4,7,9,8,20,56,78,34,2,76,84,98 };
testData.Sort(new MyCustomComparer(20));
foreach (var i in testData)
{
Console.WriteLine(i);
}
Output:
2
4
7
8
9
20
98
84
78
76
56
34
See also http://ideone.com/YlVH8i. So I don't really think this isn't "clean", but just fine.
Why don't use two steps?
var bellow = originallist.Where(i => i <= 20).OrderBy(i);
var above= originallist.Where(i => i > 20).OrderByDescending(i);
var sorted = bellow.Concat(above).ToList();
int[] a = { 4, 7, 9, 8, 20, 56, 78, 34, 2, 76, 84, 98 };
var b = a.OrderBy(i => i > 20 ? int.MaxValue - i : i);
If possible, I recommend sorting in-place. For example ( can be improved )
Array.Sort(a, (i1, i2) => (i1 > 20 ? int.MaxValue - i1 : i1) - (i2 > 20 ? int.MaxValue - i2 : i2));
[Test]
public void SortTill20AscRestDesc()
{
var src = new[] {4, 7, 9, 8, 20, 56, 78, 34, 2, 76, 84, 98};
var expected = new[] {2, 4, 7, 8, 9, 20, 98, 84, 78, 76, 56, 34};
var result = src
.Select(
i => new
{
IsAbove20 = i > 20,
Value = i
}
)
.OrderBy(e => e.IsAbove20)
.ThenBy(e => e.IsAbove20 ? int.MaxValue : e.Value)
.ThenByDescending(e => e.Value)
.Select(e=>e.Value);
Assert.That(result.SequenceEqual(expected), Is.True);
}
I have a
list<int> = {14, 24, 56,189,909,1000};
I want to collapse (group?) them by a range such that the ints that fall within the range of each other are collapse into one value.
So the results should be for range = 100
{14,24,56} //since they 24 falls within 100 of 14 and 56 falls within 100 of 24
{189}
{909, 1000} //since they fall within 100 of each other
I know this is possible using a linq group by but I am stumped by the syntax.
I have looked at this answer but cannot figure out what to use for the ranges, since I have only one range i.e. 100.
int[] values = {100, 110, 120, 130, 140, 150, 160, 170};
int[] ranges = {115, 145, 180};
var query = from value in values
group value by ranges.Where(x => value >= x)
.DefaultIfEmpty()
.Last();
foreach (var group in query)
{
Console.WriteLine("{0}: {{{1}}}", group.Key,
string.Join(", ", group));
}
Your best option is using a plain old for loop instead of linq:
var l = new[] { 14, 24, 56, 189, 909, 1000 };
var groups = new List<List<int>>();
groups.Add(new List<int>());
groups[0].Add(l[0]);
for (int i = 1; i < l.Length; i++)
{
if (l[i] - l[i - 1] > 100)
{
groups.Add(new List<int>());
}
groups[groups.Count - 1].Add(l[i]);
}
Edit: This may not apply, especially with the additional requirement in the comment as it 1) starts each group only at one point and 2) would place 150 only in the first group.
I would probably write it as so because of the "dynamic" range (and I'm not sure how Group By could be used without an equally involved bucket process). This function requires that the input is already sorted.
IEnumerable<IEnumerable<int>> GroupByStartingRange (IEnumerable<int> src) {
int? maybeStart;
while ((maybeStart = src.FirstOrDefault() != null) {
if (maybeStart.HasValue) {
var start = maybeStart.Value;
yield return src.TakeWhile(x => x <= start + 100)
src = src.SkipWhile(x => x <= start + 100);
}
}
}
Assuming your ranges are ordered:
int[] values = { 100, 110, 120, 130, 140, 150, 160, 170 };
int[] ranges = { 115, 145, 180 };
var groups = values.GroupBy(x => ranges.First(r => x <= r));
What would be the most effective way to initialize a float array of size n incrementing it every x indices using linq
for instance if array increment is 5 starting on 10 and increment size is 5 array would look like
float[] x = {10, 10, 10, 10, 10, 15, 15, 15, 15, 15, 20, 20, 20, 20, 20, 25, 25, 25, 25, 25}
You can argue what is the "best" way. Bit the way how i would do it. At first i would create a helper method that helps you to generate Enumerables.
public static IEnumerable<T> Unfold<T>(T seed, Func<T, T> accumulator) {
var nextValue = seed;
while ( true ) {
yield return nextValue;
nextValue = accumulator(nextValue);
}
}
This is a general helper function. For example with
Unfold(1, x => x*2)
it creates you a new IEnumerable where every new number is the double of the previous number. You should use the .Take() or .TakeWhile() methods to limit the amount of generated numbers. If you for example just want the first 10 numbers with power of two
Unfold(1, x => x*2).Take(10).ToList()
You get a List with [1,2,4,8,16,32,64,128,256,512]
Something like Unfold() is in general helpfull to create arbitary Enumerables.
Now to create your list. You wanted to create a list that always add 5. and then every number gets repeated 5 times, and you want reapeat that 4 times. So your first step is
Unfold(10, x => x+5).Take(4)
It will create you an IEnumerable that contains [10, 15, 20, 25]. Now the next step is to repeat every number 5 times.
You can do this with Enumerable.Repeat(). The logic would be.
Go through your list
Create a new Enumerable from every number with Enuerable.Repeat
Flatening every Enumerable to a single Enumerable
That logic above is exactly what SelectMany() does. So the solutions is
var nums = Unfold(10, x => x+5).Take(4).SelectMany(x => Enumerable.Repeat(x, 5));
Now nums is the following list [10, 10, 10, 10, 10, 15, 15, 15, 15, 15, 20, 20, 20, 20, 20, 25, 25, 25, 25, 25]
How about this:
var n = 20;
var start = 10;
var step = 5;
var increment = 5;
var x = Enumerable
.Range(0, n)
.Select(y => (float)start + increment * (y / step))
.ToArray();
Note that although I like Linq very much, I must agree with the Robert Harvey that a loop would probably be more legible in this case.
Linq probably isn't your best choice for this problem. That being said, something like this, perhaps?
var groupCount = 4;
var elementsPerGroup = 5;
var increment = 5;
var a = Enumerable
.Range(0, groupCount)
.Select(i => Enumerable
.Range(0, elementsPerGroup)
.Select(r => (float)(10 + increment * i)).ToArray())
.SelectMany(i => i)
.ToArray();
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();