how to find minimum difference object of collection in c# linq - c#

I have collection
Class MyData
{
int f1;
int f2;
int f3;
int f4;
}
var mycollection =List<MyData>();
I need to return object with minimal difference between field f1 and f3.
I tried below query
mycollection.select(obj => obj.f1 - obj.f3).Min();
But it will return the diff number. I need to return the object.
I am kind of struggling to get the object with minimum difference
I also tried this
mycollection.Select(obj => new { MyObject = obj,
diff = obj.MaxTemparature - obj.MinimumTemparature, obj
}).Min(obj => obj.diff);

Try this one
MyData myData = mycollection.OrderBy(o => (o.f1 - o.f3)).First();

You can do below steps to find object by difference F1 - F3.
Calculate difference using .Select() and store it with actual object.
Sort it using .OrderBy() and use difference as a predicate.
Get First record from it.
var result = myData.Select(x => new {diff = Math.Abs(x.F1 - x.F3), obj = x}) //Step 1
.OrderBy(y => y.diff) //Step 2
.FirstOrDefault(); //Step 3
Try it online
Or you can perform subtraction without .Select()
var result = myData.OrderBy(x => Math.Abs(x.F1 - x.F3)).FirstOrDefault();

Try like below.
mycollection.OrderBy(x => Math.Abs(x.f1 - x.f3)).FirstOrDefault();
Order your collection by difference and you want minimal difference so used Math.Abs(x.f1 - x.f3). Then take FirstOrDefault object.
Test it here

Related

LINQ - Dynamic Order By Expression

I have a little issue. Currently, I am trying to write dynamic order by query using linq.
Sql query which i am trying to implement in linq
select * from tbl
order by case when Location='Loc9787f85b-c953-4238-8bad-f712b6444443' then 1
when Location='Loc9787f85b-c953-4238-8bad-f712b6444442' then 2 end
Location value is is retrieved and saved in list. It can one or more values.
This solution seems to work for static location value. Since I retrieve location value dynamically I didnt know how to implement for dynamic location value.
var temp = tbl.OrderBy(t => t.Location== 'Loc9787f85b-c953-4238-8bad-f712b6444443' ?
1 : (t.Location== 'Loc9787f85b-c953-4238-8bad-f712b6444442' ? 2 : 3))
I will be retrieving location using this piece of code:
List<String> Location = CustomerService.GetAllLocation();
I am trying to order by using this list values. Is it possible to implement dynamic order by using list containing column value?
Use
List<String> locations = CustomerService.GetAllLocation();
var ordered = tbl.OrderBy(t => locations.Contains(t.Location) ? 0 : 1);
or, if the index should represent the priority:
var ordered = tbl
.Where(t => locations.Contains(t.Location))
.ToList() //because List.IndexOf is not supported in LINQ-TO-SQL
.OrderBy(t => locations.IndexOf(t.Location));
Rather push the logic out to a method like so:
var temp = tbl.OrderBy(t => GetOrder(t));
public int GetOrder(LocationObject t)
{
int returnValue = 0;
if (t.Location== "Loc9787f85b-c953-4238-8bad-f712b6444443")
{
returnValue = 1;
}
else if (t.Location == "Loc9787f85b-c953-4238-8bad-f712b6444442")
{
returnValue = 2;
}
else
{
returnValue = 3;
}
return returnValue;
}

Lambda/LINQ Select Minimum

I'm writing a function using the .NET GeoCoordinate class. We have an Airport class and a City class, both of which define their own GeoCoordinate.
I need to select the nearest airport relative to the city, and I am trying to do so using the GetDistanceTo() method.
What I have right now looks something like this:
Airport a = Airports.GetAllActiveAirports().Min(this.Coordinates.GetDistanceTo(n.Profile.Coordinates));
Another (working) function that retrieves a list of nearest airports by distance uses:
List<Airports> airports = Airports.GetAllActiveAirports();
var nearby =
from a in airports
where this.Coordinates.GetDistanceTo(a.Profile.Coordinates) > d
select a;
foreach(Airport a in nearby)
{
airports.Remove(a);
}
I've seen examples of doing things like this in a single line with LINQ & lambdas, but I'm not entirely sure how to execute this one...any pointers?
If I get your question, this line gets the minimum distance from Coordinates to an active airport.
Airports.GetAllActiveAirports().Min(_ => Coordinates.GetDistanceTo(_.Profile.Coordinates))
If you want the airport in question then:
var airports = Airports.GetAllActiveAirports();
var closest = airports.First(_ => Coordinates.GetDistanceTo(_.Profile.Coordinates) == airports.Min(_ => Coordinates.GetDistanceTo(_.Profile.Coordinates)))
You don't have to keep it in one line... Visual Studio won't run out of space.
An even better option, without getting the minimum in every iteration would be:
var airports = Airports.GetAllActiveAirports();
var minDistance = airports.Min(_ => Coordinates.GetDistanceTo(_.Profile.Coordinates))
var closest = airports.First(_ => Coordinates.GetDistanceTo(_.Profile.Coordinates) == minDistance)
The accepted answer causes 2 calls to GetDistance for each airport. Here's how you can do it in a single pass:
var closestAirport = Airports.GetAllActiveAirports()
.Select(x => new {
Airport = x,
Distance = this.Coordinates.GetDistanceTo(x.Profile.Coordinates)})
.Aggregate((a1, a2) => a1.Distance < a2.Distance ? a1 : a2)
.Airport;
Min will throw an InvalidOperationException if there are no values. You might try something like this. If none are found, closest will be null:
var closest = Airports.GetAllActiveAirports().OrderBy(x => x.GetDistanceTo(n.Profile.Coordinates)).FirstOrDefault();

compare none value by linq

I have an integer column(not null) in a sql server 2008 database/table, I want to retrieve it.
// go to the table and get the largest number, if none exists, use 0.
int iNumber = iContext.DetailsRecords.Max(x => x.Number); // from entity
But at the very beginning, the table is empty. I want to set is as 0.
How to change the code?
If you don't want to check if the DetailsRecords is null, but rather handle if the source sequence is empty, then use this answer (I adjusted it a bit differently for your LINQ usage):
Max or Default?
int iNumber = iContext.DetailsRecords.Select(x => (int?)x.Number).Max() ?? 0;
Enumerable.Max Method (IEnumerable<Int32>): InvalidOperationException -
source contains no elements.
Enumerable.Max Method (IEnumerable<Nullable<Int32>>): If the source sequence is empty or contains only values that are null, this function returns null.
You can use DefaultIfEmpty for this. If the sequence is empty it will return the provided item as the only item in the sequence, if not it will return the original sequence.
IEnumerable<int> numbers = new int [] { };
numbers.DefaultIfEmpty(0).Max();
try this:
int iNumber = iContext.DetailsRecords==null? iContext.DetailsRecords.Max(x => x.Number) : 0;
or
int iNumber = iContext.DetailsRecords.Any()? iContext.DetailsRecords.Max(x => x.Number) : 0; if table is not null and contains 0 records.
Use the Any function to see if there are any records:
int iNumber = iContext.DetailsRecords.Any()
? iContext.DetailsRecords.Max(x => x.Number)
: 0;
I know this already has an accepted answer, but another good way would just be FirstOrDefault().
int iNumber = iContext.DetailsRecords.OrderByDescending(x => x.Number).FirstOrDefault();
I use this way often and also can let you setup a new object if the result was null.
MyObject m = mySearch.GetItems.Where(a => a.id == id).FirstOrDefault() ?? new MyObject();

How to handle nulls in LINQ when using Min or Max?

I have the following Linq query:
result.Partials.Where(o => o.IsPositive).Min(o => o.Result)
I get an exception when result.Partials.Where(o => o.IsPositive) does not contains elements. Is there an elegant way to handle this other than splitting the operation in two and checking for null? I have a class full of operations like this one.
EDIT: The question is related with LINQ to Objects.
This is the Exception I'm getting (translated it says: The sequence is empty):
A short summary of the calculation of a Min
- No mediation (Exception!)
var min = result.Partials.Where(o => o.IsPositive).Min(o => o.Result);
This is your case: if there are no matching elements, then the Min call will raise an exception (InvalidOperationException).
- With DefaultIfEmpty() -- still troublesome
var min = result.Partials.Where(o => o.IsPositive)
.Select(o => o.Result)
.DefaultIfEmpty()
.Min();
DefaultIfEmpty will create an enumeration over the 0 element, when there are no elements in the list. How do you know that 0 is the Min or if 0 stands for a list with no elements?
- Nullable values; A better solution
var min = result.Partials.Where(o => o.IsPositive)
.Min(o => (decimal?)o.Result);
Here Min is either null (because that's equal to default(decimal?)) or the actual Min found.
So a consumer of this result will know that:
When result is null then the list had no elements
When the result is a decimal value then the list had some elements and the Min of those elements is that returned value.
However, when this doesn't matter, then min.GetValueOrDefault(0) can be called.
You can use the DefaultIfEmpty method to ensure the collection has at least 1 item:
result.Partials.Where(o => o.IsPositive).Select(o => o.Result).DefaultIfEmpty().Min();
You can't use Min (or Max) if the sequence is empty. If that shouldn't be happening, you have a different issue with how you define result. Otherwise, you should check if the sequence is empty and handle appropriately, eg:
var query = result.Partials.Where(o => o.IsPositve);
min = query.Any() ? query.Min(o => o.Result) : 0; // insert a different "default" value of your choice...
Yet another way to express it in LINQ is to use Aggregate:
var min = result.Partials
.Where(o => o.IsPositive)
.Select(o => o.Result)
.Aggregate(0, Math.Min); // Or any other value which should be returned for empty list
Since LINQ lacks methods like MinOrDefault() and MaxOrDefault(), you can create them by yourself:
public static class LinqExtensions
{
public static TProp MinOrDefault<TItem, TProp>(this IEnumerable<TItem> This, Func<TItem, TProp> selector)
{
if (This.Count() > 0)
{
return This.Min(selector);
}
else
{
return default(TProp);
}
}
}
Therefore, if the collection has values, the Min() is calculated, otherwise you get the property type's default value.
An example of use:
public class Model
{
public int Result { get; set; }
}
// ...
public void SomeMethod()
{
Console.WriteLine("Hello World");
var filledList = new List<Model>
{
new Model { Result = 10 },
new Model { Result = 9 },
};
var emptyList = new List<Model>();
var minFromFilledList = filledList.MinOrDefault(o => o.Result)); // 9
var minFromEmptyList = emptyList.MinOrDefault(o => o.Result)); // 0
}
NOTE 1: you don't need to check if the This parameter is null: the invoked Count() already checks that, and it throws the same Exception that you would throw.
NOTE 2: This solution is good only in situations where the Count() method is cheap to call. All .NET collections are fine since they are all very efficient; it could be a problem for particular customized/non-standard collections.

c# Linq differed execution challenge - help needed in creating 3 different lists

I am trying to create 3 different lists (1,2,3) from 2 existing lists (A,B).
The 3 lists need to identify the following relationships.
List 1 - the items that are in list A and not in list B
List 2 - the items that are in list B and not in list A
List 3 - the items that are in both lists.
I then want to join all the lists together into one list.
My problem is that I want to identify the differences by adding an enum identifying the relationship to the items of each list. But by adding the Enum the Except Linq function does not identify the fact (obviously) that the lists are the same. Because the Linq queries are differed I can not resolve this by changing the order of my statements ie. identify the the lists and then add the Enums.
This is the code that I have got to (Doesn't work properly)
There might be a better approach.
List<ManufactorListItem> manufactorItemList =
manufactorRepository.GetManufactorList();
// Get the Manufactors from the Families repository
List<ManufactorListItem> familyManufactorList =
this.familyRepository.GetManufactorList(familyGuid);
// Identify Manufactors that are only found in the Manufactor Repository
List<ManufactorListItem> inManufactorsOnly =
manufactorItemList.Except(familyManufactorList).ToList();
// Mark them as (Parent Only)
foreach (ManufactorListItem manOnly in inManufactorsOnly) {
manOnly.InheritanceState = EnumInheritanceState.InParent;
}
// Identify Manufactors that are only found in the Family Repository
List<ManufactorListItem> inFamiliesOnly =
familyManufactorList.Except(manufactorItemList).ToList();
// Mark them as (Child Only)
foreach (ManufactorListItem famOnly in inFamiliesOnly) {
famOnly.InheritanceState = EnumInheritanceState.InChild;
}
// Identify Manufactors that are found in both Repositories
List<ManufactorListItem> sameList =
manufactorItemList.Intersect(familyManufactorList).ToList();
// Mark them Accordingly
foreach (ManufactorListItem same in sameList) {
same.InheritanceState = EnumInheritanceState.InBoth;
}
// Create an output List
List<ManufactorListItem> manufactors = new List<ManufactorListItem>();
// Join all of the lists together.
manufactors = sameList.Union(inManufactorsOnly).
Union(inFamiliesOnly).ToList();
Any ideas hot to get around this?
Thanks in advance
You can make it much simplier:
List<ManufactorListItem> manufactorItemList = ...;
List<ManufactorListItem> familyManufactorList = ...;
var allItems = manufactorItemList.ToDictionary(i => i, i => InheritanceState.InParent);
foreach (var familyManufactor in familyManufactorList)
{
allItems[familyManufactor] = allItems.ContainsKey(familyManufactor) ?
InheritanceState.InBoth :
InheritanceState.InChild;
}
//that's all, now we can get any subset items:
var inFamiliesOnly = allItems.Where(p => p.Value == InheritanceState.InChild).Select(p => p.Key);
var inManufactorsOnly = allItems.Where(p => p.Value == InheritanceState.InParent).Select(p => p.Key);
var allManufactors = allItems.Keys;
This seems like the simplest way to me:
(I'm using the following Enum for simplicity:
public enum ContainedIn
{
AOnly,
BOnly,
Both
}
)
var la = new List<int> {1, 2, 3};
var lb = new List<int> {2, 3, 4};
var l1 = la.Except(lb)
.Select(i => new Tuple<int, ContainedIn>(i, ContainedIn.AOnly));
var l2 = lb.Except(la)
.Select(i => new Tuple<int, ContainedIn>(i, ContainedIn.BOnly));
var l3 = la.Intersect(lb)
.Select(i => new Tuple<int, ContainedIn>(i, ContainedIn.Both));
var combined = l1.Union(l2).Union(l3);
So long as you have access to the Tuple<T1, T2> class (I think it's a .NET 4 addition).
If the problem is with the Except() statement, then I suggest you use the 3 parameter override of Except in order to provide a custom IEqualityComparer<ManufactorListItem> compare which tests the appropriate ManufactorListItem fields, but not the InheritanceState.
e.g. your equality comparer might look like:
public class ManufactorComparer : IEqualityComparer<ManufactorListItem> {
public bool Equals(ManufactorListItem x, ManufactorListItem y) {
// you need to write a method here that tests all the fields except InheritanceState
}
public int GetHashCode(ManufactorListItem obj) {
// you need to write a simple hash code generator here using any/all the fields except InheritanceState
}
}
and then you would call this using code a bit like
// Identify Manufactors that are only found in the Manufactor Repository
List<ManufactorListItem> inManufactorsOnly =
manufactorItemList.Except(familyManufactorList, new ManufactorComparer()).ToList();

Categories