I'm trying to find an item in a list of values based on another value using a lambda expression using the Find method. In this example I'm expecting to get back -1000, but for the life of me, I just can't come up with the proper lamda expression. If that sounds confusing I hope the code and comments below explain it better.
TIA.
using System;
using System.Collections.Generic;
namespace TestingStuff {
class Program {
static void Main(string[] args) {
double amount = -200;
//The Range of values
List<MyValue> values = new List<MyValue>();
values.Add(new MyValue(-1000));
values.Add(new MyValue(-100));
values.Add(new MyValue(-10));
values.Add(new MyValue(0));
values.Add(new MyValue(100));
values.Add(new MyValue(1000));
//Find it!!!
MyValue fVal = values.Find(x => (x.Value > amount) && (x.Value < amount));
//Expecting -1000 as a result here since -200 falls between -1000 and -100
//if it were -90 I'd expect -100 since it falls between -100 and 0
if (fVal != null)
Console.WriteLine(fVal.Value);
Console.ReadKey();
}
}
public class MyValue {
public double Value { get; set; }
public MyValue(double value) {
Value = value;
}
}
}
Mmm let me put my intentions a little clearer by specifying all the expected results.
-1000 and less to -101 should give -1000
-100 to - 11 should give -100
-10 to -1 should give -10
0 to 9 should give 0
10 to 99 should give 10
100-999 should give 100
1000 or more should give 1000
This should work:
values.FindLast(x => amount >= x.Value);
You did a logical mistake ... a value can't be > -200 AND < -200 at the same time .. U need the OR expression ( "||" )
MyValue fVal = values.Find(x => (x.Value > amount) || (x.Value < amount));
But if you expect to get -1000 this expression is also wrong
MyValue fVal = values.Find(x => (x.Value < amount));
Because -1000 is SMALLER than -200
EDIT : Ok I think I missunderstood your intention. But the way you want to select your value doesn't seem logical to me. Do you want the next smaller value ?
I'm making the assumption that if you used the value +90, you'd expect 100 and not zero, as well as if you use 200, you're expecting 1000 and not 100.
MyValue fVal = values
.Where(x => amount > 0 ? x.Value > amount : x.Value < amount)
.OrderBy(x => amount > 0 ? x.Value : -x.Value).First();
Making the same assumption as Darksider Another option would be
MyValue fVal = values.Find(x => Math.Abs(x.Value) > amount && (x.Value<0 == amount<0));
of course this relies on the list already being sorted. Darksider's solution may be better if the list might not be sorted alreday.
Related
In the Linq code below, the count is 16 and the sum is 21 which are correct. However, the score always shows as 100. It should be 76.19. What is happening?
Also, I tried score = sum/count, but I can't seem to use the variable inside the new section. Any suggestions?
.GroupBy(g => g.YR_MNTH)
.Select(x =>
new
{
count = x.Count(),
sum = x.Sum(i=>i.SCORE >= 95? 1:0),
score = (decimal)Math.Round((decimal)(x.Sum(i => i.SCORE >= 95 ? 1 : 0) / x.Count()) * 100, 2)
});
Performing math on integers results in integers. So if you do something like this:
1 / 2
The result will not be 0.5, it will just be 0. So this:
x.Sum(i => i.SCORE >= 95 ? 1 : 0) / x.Count()
Will result in an integer. Later casting that integer to a decimal won't change its value after the fact. You need to cast the individual values before performing math on them:
(decimal)x.Sum(i => i.SCORE >= 95 ? 1 : 0) / (decimal)x.Count()
The problem is that x.Count() is an int and x.Sum(i=>i.SCORE >= 95? 1:0) is an int. An int divided by an int is an int. 21 divided by 16 in integer division is 1 which you are then multiplying by 100. You need to move your decimal cast and place it on one of you operands inside the parenthesis; like this, for example: (decimal)x.Sum(i => i.SCORE >= 95 ? 1 : 0). A decimal divided by an int will result in a decimal so you will be back in business.
On a side note performing these aggregations multiple times is not the most efficient thing to do.
I know this question has been asked before, but the other questions are only about finding the CLOSEST. I dont want that. I need the LOWEST between two values. For example if this is the list:
Code from How to get the closest number from a List<int> with LINQ?:
List<int> numbers = new List<int>();
numbers.Add(2);
numbers.Add(5);
numbers.Add(7);
numbers.Add(10)
and the number to find is 9, I want it to return the 7 item, not 10 even though its closer. This code finds the closest, but any time I change it to find the lowest in the range it breaks for other situations where the inputted number is one of the numbers in the list(for example 7 or 10):
list.Aggregate((x,y) => Math.Abs(x-number) < Math.Abs(y-number) ? x : y);
How do I alter that code to support what I need to do?
I know I could use binarysearch... I don't want to use that, I want to use linq if possible.
var numbers = new List<int> { 2, 5, 7, 10 };
var seven = numbers.Where(n => n <= 9).Max();
If you have to consider cases where the list will not any number closest, the code would look like,
private static int? GetClosest(List<int> numbers, int number)
{
var shorterEnumerable = numbers.Where(x => x <= number);
var shorterArray = shorterEnumerable as int[] ?? shorterEnumerable.ToArray();
if (shorterArray.Length > 1)
return shorterArray.Max();
return null;
}
even #danielnixon answer is good, this uses agregate
int? closerLow = (int?) list.Aggregate((x,y) => Math.Abs(x-number) < Math.Abs(y-number)
? (x > number ? y : x )
: (y > number ? x : y));
if (closerLow > number) closerLow = null;
I have an IEnumerable<float> containing distinct values found in a three dimensional array.
Given a test value, I want to take two elements from my distinct IEnumerable, the closest value which is greater than or equal to the test value, and the closest value which is less than the test.
In other words, if my test value is 80.5, and my list contains:
1.0
1.65
2.345
99.439
Then I want an IEnumerable<float> or a Tuple<float,float> back which contains 2.345 and 99.439.
Is there a LINQ statement or combination of such which will do that? An approach without LINQ?
Without using LINQ and assuming that there are only values > 0 in an input collection. There's no need to sort the collection first.
public Tuple<float, float> GetClosestValues(IEnumerable<float> values, float target)
{
float lower = 0;
float upper = Single.MaxValue;
foreach (var v in values)
{
if (v < target && v > lower) lower = v;
if (v > target && v < upper) upper = v;
}
return Tuple.Create(lower, upper);
}
In a tuple:
var t = Tuple.Create(list.Where(x => x <= value).Max(),
list.Where(x => x >= value).Min()
);
Although you don't state what the output should be if the value is in the list - in this case it would be a tuple with the same value for both "nodes"
Tuple.Create(
values.OrderBy(i => i)
.SkipWhile(i => i < test)
.FirstOrDefault(),
values.OrderByDescending(i => i)
.SkipWhile(i => i >= test)
.FirstOrDefault());
Sort (ascending), skip all values less than test, take the first value greater than or equal to test.
Sort (descending), skip all values greater than or equal to test, take the first value less than test.
double[] data = { .1,5.34,3.0,5.6 };
double test = 4.0;
var result = data.Aggregate(Tuple.Create(double.MinValue, double.MaxValue),
(minMax, x) => Tuple.Create(
x < test && x > minMax.Item1 ? x : minMax.Item1,
x >= test && x < minMax.Item2 ? x : minMax.Item2));
Assuming your list is sorted (and few other assumptions on the requirements (such as what happens if there is a direct hit).
float prev=0;
foreach(float item in YourIEnumerableFloatVar)
{
if (item > target)
{
return new Tuple<float, float> (prev, item);
}
prev = item;
}
I have a list of objects
public class foo
{
public decimal val1 {get;set;}
public decimal val2 {get;set;}
}
I val1 and val2 can contain both negative or positive values.
if I have a List<foo>items is there a clean way I can sort them so that a negative value in either val1 or val2 are not the first or last item in the list.
My list size can very from 1 - 100. if it is less then 3 I do not need to sort. But if it is >= 3 I need to make sure any negative values are not first or last in the list.
You would try to push a "positive" value to the head and the tail of the list if they exist:
if (myList.Count > 2)
{
//push a positive to the head of the list
var firstPositive = myList.FirstOrDefault(x => x.val1 > 0 && x.val2 > 0);
if (firstPositive != null)
{
myList.Remove(firstPositive);
myList.Insert(0, firstPositive);
}
//push a positive to the tail of the list
var secondPositive = myList.Skip(1).FirstOrDefault(x => x.val1 > 0 && x.val2 > 0);
if (secondPositive != null)
{
myList.Remove(secondPositive);
myList.Add(secondPositive);
}
}
Create your own MyList:List<decimal> class and override Add(..), Insert(...), Remove(..) and other methods to fit your needs.
Or you can use ObservableCollection of decimal and listen for CollectionChanged event.
I need to write a function that returns the first perfect square that is greater than its integer argument. A perfect square is an integer that is equal to some integer squared. For example 16 is a perfect square because 16 = 4 * 4. However 15 is not a perfect square because there is no integer n such that 15 = n*n.
public static int NextPerfectSquare(int inputNumber)
{
if (inputNumber < 0) return 0;
inputNumber++;
var result = Math.Sqrt(inputNumber);
var resultnumber = (int) result;
if (resultnumber == result) return inputNumber;
resultnumber++;
return resultnumber * resultnumber;
}
Is this right?
The basic solution looks good. You may want to consider:
Should comments be added to this function? Maybe not for an exam, but worth considering.
Use consistent casing for your parameters/local variables. Consider whether they could be named more clearly.
What about boundary conditions? You've got the negative case covered, but what if inputNumber is close to int.MaxValue so that the next perfect square would be > MaxValue?
Looks right to me. Handles negative numbers, handles some arbitrary value which is not a perfect square properly, handles perfect squares properly, so I'll go with yes.
Kind of.
But I'm loathe to leave it at that because you could have verified this yourself quite easily by running some tests.
System.Console.WriteLine("-10 => {0}", NextPerfectSquare(-10));
System.Console.WriteLine("0 => {0}", NextPerfectSquare(0));
System.Console.WriteLine("1 => {0}", NextPerfectSquare(1));
System.Console.WriteLine("15 => {0}", NextPerfectSquare(15));
System.Console.WriteLine("21 => {0}", NextPerfectSquare(21));
System.Console.WriteLine("24 => {0}", NextPerfectSquare(24));
System.Console.WriteLine("36 => {0}", NextPerfectSquare(36));
System.Console.WriteLine("Max => {0}", NextPerfectSquare(int.MaxValue));
System.Console.WriteLine("Min => {0}", NextPerfectSquare(int.MinValue));
-10 => 0
0 => 1
1 => 4
15 => 16
21 => 25
24 => 25
36 => 49
Max => 1
Min => 0
So you could probably optimize it a little for bonus points?
Make it safe for large numbers. i.e. long/Int64
Make it safe from max value overflows. (try entering int.MaxValue as your input)
Seems to be working correct.
I would personally go for something like:
public static int Next(int inputNumber)
{
if (inputNumber < 0) return 0;
int perfectWidth = (int)Math.Floor(Math.Sqrt(inputNumber));
return (int)Math.Pow(perfectWidth + 1, 2);
}
as i think it shows the logic a bit clearer. But that might be my personal preferences of course ;)
You can reduce your code as
public static int NextPerfectSquare(int inputNumber)
{
if (inputNumber < 0) return 0;
var result = Math.Sqrt(inputNumber);
var resultnumber = (int) result;
resultnumber++;
return resultnumber * resultnumber;
}