Linq data type comparison with Double - c#

In my Linq query I have a where statement that looks like this
&& vio.Bows.Any(nw => nw.XCoordinate.Equals(currVio.XCoordinate)))
values are
nw.XCoordinate = 4056.48751252685
currVio.XCoordinate = 4056.488
Thus the statement of Equals is not working, what is the easiest way to round?
public double XCoordinate { get; set; }

You can use the usual way of comparing double for proximity by calculating the absolute difference, and comparing it to a small value:
Math.Abs(x - y) < 1E-8 // 1E-8 is 0.00000001
For example, you can use this approach in a LINQ query like this:
&& vio.Bows.Any(nw => Math.Abs(nw.XCoordinate-currVio.XCoordinate) < 0.001)

You could also use Math.Round i.e.:
double x = 4056.48751252685;
var newx = Math.Round(x,3);
So if you knew you always wanted 3 decimal places you could:
&& vio.Bows.Any(nw => Math.Round(nw.XCoordinate,3).Equals(math.Round(currVio.XCoordinate,3))))
You could go further and implement IEquetable, and override the equals function, determine which of the two values has the least number of decimal places, round both to that value and compare.

Related

How to get biggest element in HashSet of object by field?

Suppose I have the class
public class Point {
public float x, y, z;
}
And I've created this hashset:
HashSet<Point> H;
How can I get the element of H with the biggest z? It doesn't need necessarily to use Linq.
You can use Aggregate to mimic MaxBy functionality (note that you need to check if collection has any elements first):
var maxByZ = H.Aggregate((point, point1) => point.z > point1.z ? point : point1);
When .NET 6 is out it should have built in MaxBy.
You could do this:
int maxZ = H.Max(point => point.Z);
var maxPointByZ = H.Where(point => point.Z == maxZ).FirstOrDefault();
This works by first retrieving the largest value of Z in the set:
H.Max(point2 => point2.Z) //Returns the largest value of Z in the set
And then by doing a simple where statement to get the record where Z is equal to that value. If there are multiple values, it will get the first one, so you may want to sort the enumerable in advance.

What is best practice to use when checking if decimal matches a query

I have a for loop that loops a list of transactions, which all contains amount. If the amount is correct, I want that transaction to be included in a new list.
So in code:
decimal searchAmount = 33.03;
foreach (var order in listOforders)
{
if(order.amount == searchAmount)
{
addOrderToList()
}
}
The currency used doesn't use more than two decimals, so that's okay. These three scenarios, should all add the order to the list.
order.Amount = 33.03
search.Amount = 33
order.Amount = 33.03
search.Amount = 33.03
order.Amount = 33.99
search.Amount = 33.9
Note:
This is a search. When the customer comes back, and says "I have a problem with the product I purchased, and it's not purchased on a registered customer", searching for the amount on the customers bank receipt is a great function. This is a retail brick and mortar store scenario, so some customers choose to not register themselves.
If you want to discard the fractional part completely, using a combination of LINQ and Math.Truncate
var orders = listOfOrders.Where(o => Math.Truncate(o.amount) == Math.Truncate(searchAmount))
.ToList();
Math.Truncate returns the integral part of a given decimal, Where selects only appropriate orders (do read up on LINQ's deferred execution if you don't know it) and ToList materializes the query into a list.
EDIT: given your edit, this is probably what you're looking for:
var orders
= listOfOrders.Where(
o => Math.Truncate(o.amount) == Math.Truncate(searchAmount)
&& o.amount.ToString(CultureInfo.InvariantCulture)
.StartsWith(searchAmount.ToString(CultureInfo.InvariantCulture)))
.ToList();
This first verifies if the integral part of the numbers match and then uses string comparison to check if the actual amount starts with what was inputted (by your lazy user).
Use your if condition like this. Round to 2 decimal places and compare.
if((Math.Round(order.amount,2) - Math.Round(searchAmount,2)) <= 0.9M)
{
addOrderToList();
}
What if you use Math.Truncate(number)? Just like:
if(Math.Truncate(order.amount) == Math.Truncate(searchAmount))
If am right you need to do define some maximum difference constant and use it something like that
decimal maxDiff = 0.03;
decimal searchAmount = 33.03;
var result = listOfOrders.Where(o => Math.Abs(o.Amount - searchAmount) <= maxDiff);
You need to use Math.Floor method to match all numbers i.e absolute value
decimal searchAmount = 33.03;
var temp = Math.Floor(searchAmount);
foreach (var order in listOforders)
{
if(Math.Floor(order.amount) == temp)
{
addOrderToList()
}
}
There is no need for any call to Math as a cast to int will do the same. Your code could be changed to;
int searchAmount = 33;
listOforders.Where(o => (int)o.Amount == searchAmount)
.ForEach(o => addOrderToList());

How to convert from decimal to double in Linq to Entity

Suppose we have table T which has two columns A and B with float and money types respectively. I want to write a linq query like following T-SQL statement:
Select A, B, A * B as C
From SomeTable
Where C < 1000
I tried to cast like following
var list = (from row in model.Table
where ((decimal)row.A) * row.B < 1000
select new { A = row.A,
B = row.B ,
C = ((decimal)row.A) * row.B}
).ToList();
but it does not allow the cast operation. It throw an exception:
Casting to Decimal is not supported in Linq to Entity queries, because
the required precision and scale information cannot be inferred.
My question is how to convert double to decimal in Linq? I don't want to fetch data from database.
Update:
I notice the converting decimal to double works but reverse operation throws the exception. So,
Why can't we convert double to decimal? Does Sql server do the same mechanism in t-sql too? Doesn't it affect precision?
The difference between a float (double) and a decimal, is that a float is decimal precise. If you give the float a value of 10.123, then internally it could have a value 10.1229999999999, which is very near to 10.123, but not exactly.
A decimal with a precision of x decimals will always be accurate until the x-th decimal.
The designer of your database thought that type A didn't need decimal accuracy (or he was just careless). It is not meaningful to give the result of a calculation more precision than the input parameters.
If you really need to convert your result into a decimal, calculate your formula as float / double, and cast to decimal after AsEnumerable:
(I'm not very familiar with your syntax, so I'll use the extension method syntax)
var list = model.Table.Where(row => row.A * row.B < 1000)
.Select(row => new
{
A = row.A,
B = row.B,
})
.AsEnumerable()
.Select(row => new
{
A = row.A,
B = row.B,
C = (decimal)row.A * (decimal)row.B,
});
Meaning:
From my Table, take only rows that have values such that row.A * row.B
< 1000.
From each selected row, select the values from columns A and B.
Transfer those two values to local memory (= AsEnumerable),
for every transferred row create a new object with three properties:
A and B have the transferred values.
C gets the the product of the decimal values of transferred A and B
You can avoid AsEnumerable() explaining to Entity how many fractional digits you want.
var list = (from row in model.Table
where ((decimal)row.A) * row.B < 1000
select new { A = row.A,
B = row.B ,
C = (((decimal)((int)row.A)*100))/100) * row.B}
).ToList();

ILNumerics using subarrays and matfiles with structs

First question: Can ILNumerics read matfiles with struct? I couldnt make it work.
I then split the file in matlab and I would like to use it for calculations. but i have problems with the subarray function. I would like to do this:
using (ILMatFile matRead = new ILMatFile(#"C:\Temp\Dates.mat"))
{
ILArray<double> Dates = matRead.GetArray<double>("Dates");
double x = 736055-1;
double y = 736237+1;
ILArray<ILLogical> LogDates = (Dates > x && Dates < y);
}
using (ILMatFile matRead = new ILMatFile(#"C:\Temp\Power.mat"))
{
ILArray<double> power = matRead.GetArray<double>("Power");
ILArray<double> tpower = power[LogDates, full];
double avgpower = tpower.Average();
Console.WriteLine(avgpower.ToString());
Console.ReadKey();
}
This doesnt work for a number of reasons. The logical doesnt take my syntax and I dont really get why. But also the subarry in the second block doesnt work, it doesnt know the full keyword (even though the documentation says it shouldand also it doesnt like the logical. obviously I want to average tpower over all columns and only those rows where the logical condition is one.
thanks.
nik
ILLogical is an array itself. You use it like that:
ILLogical LogDates = ILMath.and(Dates > x, Dates < y);
If you still experiencing problems with the subarray, try:
ILArray<double> tpower = power[ILMath.find(LogDates), ILMath.full];
Only, if your class is derived from ILMath, you can ommit the ILMath. identifier! Otherwise, string subarray definitions are sometimes shorter:
ILArray<double> tpower = power[ILMath.find(LogDates), ":"]
In order to take the average over selected rows, reducing to one:
double avgpower = tpower.Average(); // Linq version
double avgpower = (double)ILMath.sumall(tpower) / tpower.S.NumberOfElements; // prob. faster on large data

Alternatives to nested Select in Linq

Working on a clustering project, I stumbled upon this, and I'm trying to figure out if there's a better solution than the one I've come up with.
PROBLEM : Given a List<Point> Points of points in R^n ( you can think at every Point as a double array fo dimension n), a double minDistance and a distance Func<Point,Point,double> dist , write a LINQ expression that returns, for each point, the set of other points in the list that are closer to him than minDistance according to dist.
My solution is the following:
var lst = Points.Select(
x => Points.Where(z => dist(x, z) < minDistance)
.ToList() )
.ToList();
So, after noticing that
Using LINQ is probably not the best idea, because you get to calculate every distance twice
The problem doesn't have much practical use
My code, even if bad looking, works
I have the following questions:
Is it possible to translate my code in query expression? and if so, how?
Is there a better way to solve this in dot notation?
The problem definition, that you want "for each point, the set of other points" makes it impossible to solve without the inner query - you could just disguise it in clever manner. If you could change your data storage policy, and don't stick to LINQ then, in general, there are many approaches to Nearest Neighbour Search problem. You could for example hold the points sorted according to their values on one axis, which can speed-up the queries for neighbours by eliminating early some candidates without full distance calculation. Here is the paper with this approach: Flexible Metric Nearest Neighbor Classification.
Because Points is a List you can take advantage of the fact that you can access each item by its index. So you can avoid comparing each item twice with something like this:
var lst =
from i in Enumerable.Range(0, Points.Length)
from j in Enumerable.Range(i + 1, Points.Length - i - 1)
where dist(Points[i], Points[j]) < minDistance
select new
{
x = Points[i], y = Points[j]
};
This will return a set composed of all points within minDistance of each other, but not exactly what the result you wanted. If you want to turn it into some kind of Lookup so you can see which points are close to a given point you can do this:
var lst =
(from i in Enumerable.Range(0, Points.Length)
from j in Enumerable.Range(i + 1, Points.Length - i - 1)
where dist(Points[i], Points[j]) < minDistance
select new { x = Points[i], y = Points[j] })
.SelectMany(pair => new[] { pair, { x = pair.y, y = pair.x })
.ToLookup(pair => pair.x, pair => pair.y);
I think you could add some bool Property to your Point class to mark it's has been browsed to prevent twice calling to dist, something like this:
public class Point {
//....
public bool IsBrowsed {get;set;}
}
var lst = Points.Select(
x => {
var list = Points.Where(z =>!z.IsBrowsed&&dist(x, z) < minDistance).ToList();
x.IsBrowsed = true;
return list;
})
.ToList();

Categories