C# list sort with OR condition - c#

I'm trying to sort a list that comes from my database. If all fields in my OrderFunds column are null, I want to sort by another column. Can anyone tell me how to do this?
This is my code:
List<Fund> funds = await fundSupervisor.List().Where(fund => fund.IsActive)
.OrderBy(fund => fund.OrderFunds)
Or, my OrderBy clause can allow null values. That would help me too.

This is the solution
List<Fund> funds = fundSupervisor
.Where(w => w.IsActive)
.OrderBy(w => w.OrderFunds)
.ThenBy(w => w.OtherField).ToList();
ThenBy() allows you specify other fields to sort your list.

If you need to sort totally different if, and only if all columns are null, you need to check that in advance.
List<Fund> funds = await fundSupervisor.List().Where(fund => fund.IsActive)
if(funds .Any(item => item.OrderFunds != null))
funds = funds.OrderBy(fund => fund.OrderFunds);
else
funds = funds.OrderBy(fund => fund.SomethingElse);
Otherwise, if you want to sort by another value if OrderFunds is null, then you can simply use a condition inside the OrderBy.
var orderedFunds = funds.OrderBy(fund => fund.OrderFunds ?? SomeOtherValue);
If you want to sort by another value as secondary sorting, use a ThenBy. That sorts all values by the ThenBy-value if there are multiple same values in the OrderBy-operation.
var orderedThenByFunds = funds.OrderBy(fund => fund.OrderFunds).ThenBy(fund => fund.SomeOtherValue);
Consider that these lines are different:
Say you have 3 items (assumed OrderFunds is int?:
Fund1 { OrderFunds = 1, SomeOtherValue = 8 }
Fund2 { OrderFunds = 3, SomeOtherValue = 6 }
Fund3 { OrderFunds = null, SomeOtherValue = 4 }
Fund4 { OrderFunds = null, SomeOtherValue = 2 }
Then orderedFunds would return Fund1(1), Fund4(2[SomeOtherValue]), Fund2(3), Fund3(4[SomeOtherValue]) and orderedThenByFunds would return Fund1(1,8), Fund2(3,8), Fund4(null,2), Fund3(null,2). The values in braces are the values ordered by.

Related

C# - LINQ - specific query select

I would like to get a specific select with the best performance possible
I currently have a code like this:
var getData = from x in dbContex.MyTable
where x.Car == "audi"
select x;
MyTable has three columns:
Car
ProductionYear
Color
I would like to download a specific vehicle brand with the year of manufacture that has a specific color.
I am currently doing it with reference to the code above:
foreach (var item in getData)
{
if (item.color == "blue") item.ProductionYear = "1999";
if (item.color == "red") item.ProductionYear = "2003";
// etc (...)
}
I would like to assign a specific year for a given car and color if I find it.
A very prosaic example above.
Problem - I currently have 100 colors and only want to check 10 colors.
How can I do it faster so as not to search the entire list and not to use IF statements?
Create an array of the colors, then use Contains method in the predicate expression:
var colors = new [] { "red", "blue" };
var results = dbContext.MyTable
.Where( x => "audi" == x.Car
&& colors.Contains( x.color ) );
Then use a switch statement to assign your years:
foreach(var car in results)
{
car.ProductionYear = car.color switch
{
"blue" => 1999,
"red" => 2003,
_ => // unexpected result, throw exception
}
}
You could also define the years with the colors then work the year assignment into the LINQ query:
var colorYears = new []
{
new {
color = "red",
ProductionYear = 2003,
},
...
};
var colors = colorYears.Select( x => x.color ).ToArray();
var results = (...query from above...)
// enumerate
.ToArray()
// join datasource results with your colorYears array
// on the `color` property
.Join( colorYears,
c => c.color,
cy => cy.color,
( c, cy ) =>
{
// assign the configured production year for the color
c.ProductionYear = cy.ProductionYear;
// return the entity
return c;
} );
There is no scenario, where you don`t use if or switch clause. You want to change data, which is not LINQ case. Only thing I would do - go to switch, rather than If clause.
I don't want to create a new thread so I will write here - a question about LINQ of course and data comparison in the array
I have this code:
var dataTest = (dbContex.MyTable.Where(x => x.col1 == str1
&& x.col2 == str2 && x.col3 == str3).Select(x => x.ID ).FirstOrDefault());
I want to check if there is such a row for these three fields and get its ID - everything is fine when I have values, but when any field is NULL - it cannot compare it and returns information that there is no such record.
the goal is to check if there is a duplicate with these values ​​in MyTable based on these three fields - except that each of these fields can be NULL
how best to compare NULL?

Comparing sql with c# list in query?

I have a problem with comparing a value in my sql database with a list of object in my code.
The object does not exist in DB
Example of an object in the list:
{
public long CompareId
public bool HasAccess
}
I'm using SQLBuilder in c#
And then I want to make a query that says something like this:
In made up code
SELECT * FROM EntityPermission
WHERE EntityPermission.HasAccess = listOfObjects.Where(obj => obj.CompareId == EntityPermission.CompareId).HasAccess
In more made up code but with sql builder
query.WHERE("(EntityPermission.HasAccess = {0})", listOfObjects.Where(obj => obj.CompareId == EntityPermission.CompareId).HasAccess)
I'm fully aware of that I'm mixing c# and sql here, but it was the best way I could explain what I want to accomplish.
In words
I want to find the EntityPermission where the HasAccess column is equal to the HasAccess property of the object where they have the same Id.
Really thankful for all help!
I want to find the EntityPermission where the HasAccess column is equal to the HasAccess property of the object where they have the same Id.
So you have a table EntityPermissions. Every EntityPermission in this table has at least a Boolean property HasAccess, and a primary key in long property Id
Furthermore you have a list of objects, where every object has at least an CompareId and a HasAccess.
If I read your requirement correctly, you want all EntityPermissions with Id that is also a CompareId in your list, and that have equal HasAccess value.
So if your list has values:
{10, false}, {11, true}, {12, false},
And you have EntityPermissiont:
Id HasAccess
09 true don't want this one, Id is not in the list
10 true don't want this one, Id is in the list, but HasAccess incorrect
11 true I want this one: Id is in the list, HasAccess is correct
12 false I want this one: Id is in the list, HasAccess is correct
Normally you would use Where(x => y.Contains(x)) for this. The problem is that with this you can only select on one property.
var checkValues = new
{
new {CompareId = 10, HasAccess = false},
new {CompareId = 11, HasAccess = true},
new {CompareId = 12, HasAccess = false},
}
var result = dbContext.EntityPermissions.Select(entityPermission => new
{
ValueToCompare = new
{
CompareId = entityPermission.Id,
HasAccess = entityPermission.HasAccess,
},
Original = entityPermission,
})
// keep only those selected items that have a ValueToCompare in CheckValues
.Where(selectedItem => checkValues.Contains(selectedItem.ValueToCompare)
// from the remaining items, extract the original EntityPermission
.Select(selectedItem => selectedItem.Original);
What you're looking for is the SQL WHERE...IN () syntax.
If you're using a tool that produces SQL, what you want to do is something like this:
1) get the list of values you want to compare
2) Create a string like the following from them:
"('value1','value2','value3')"
3) then produce a query that looks like this:
SELECT * FROM EntityPermission
WHERE EntityPermission.HasAccess
IN ('value1','value2','value3')
for an ORM like Entity Framework, NHibernate, etc, you can do the following:
var results = db.EntityPermissions
.Where(x => listOfObjects
.Where(obj => obj.CompareId == EntityPermission.CompareId)
.Select(y => y.HasAccess)
.Contains(x.HasAccess))
You could do this with a table-valued-parameter and user-defined-type, then inner join:
SELECT * FROM EntityPermission ep
INNER JOIN #foo f ON f.Id = ep.Id AND f.HasAccess = ep.HasAccess
However: UDTs and TVPs are really very awkward to work with; frankly, I'd be tempted to just create two concatenated strings:
string with = string.Join(",", list.Where(x => x.HasAccess).Select(x => x.Id));
string without = string.Join(",", list.Where(x => !x.HasAccess).Select(x => x.Id));
and pass that down as parameters to use with string_split:
SELECT *
FROM EntityPermission
WHERE (Id in (select value from string_split(#with, ',')) and HasAccess = 1)
OR (Id in (select value from string_split(#without, ',')) and HasAccess = 0)
You can first get your sql query in a result table then use LINQ to get your intended values. I know it is not most effective way but it could work.
public virtual List<YOUR_DTO> ExampleOperation(YOUR_DTO dto)
{
sqlText="SELECT * FROM EntityPermission ";
dbComm = db.GetSqlStringCommand(sqlText);
DataTable table = this.Database.ExecuteDataSet(dbComm).Tables[0];
List<YOUR_DTO> result = new List<YOUR_DTO>();
foreach (DataRow row in table.Rows)
{
result.Add(new YOUR_DTO()
{
...
});
}
//LINQ
result = result.Where(obj => obj.CompareId == EntityPermission.CompareId).HasAccess;
return result;
}

How to use the let clause in C#

The code below works perfectly in LINQPad, but when I implement it in Visual Studio it doesn't pull out any data. When I comment the let clause, and the cycles (1 to 5), I get the all the other data (Pin ...Notes). Can someone let me know how to implement the let clause in C#?
public List<RouteStatus> Route_AB_List(int yardId, int siteTypeId)
{
using (var context = new COESystemContext())
{
var RouteList = from site in context.Sites
where site.YardID == yardId && site.SiteTypeID == siteTypeId && site.Season.SeasonYear == DateTime.Now.Year
orderby site.Community.Name ascending
let Cycles = site.JobCards
.Where(job => job.OperationID == 1)
.OrderByDescending(job => job.ClosedDate.HasValue)
.ThenBy(job => job.ClosedDate)
.Select(job => new { Date = job.ClosedDate })
select new RouteStatus
{
Pin = site.Pin,
Community = site.Community.Name,
Neighbourhood = site.Neighbourhood,
Address = site.StreetAddress,
Area = site.Area,
Notes = site.Notes,
Cycle1 = Cycles.FirstOrDefault().Date,
Cycle2 = Cycles.FirstOrDefault().Equals(null) ? (DateTime?)null : Cycles.Skip(1).FirstOrDefault().Date,
Cycle3 = Cycles.Skip(2).FirstOrDefault().Equals(null) ? (DateTime?)null : Cycles.Skip(2).FirstOrDefault().Date,
Cycle4 = Cycles.Skip(3).FirstOrDefault().Equals(null) ? (DateTime?)null : Cycles.Skip(3).FirstOrDefault().Date,
Cycle5 = Cycles.Skip(4).FirstOrDefault().Equals(null) ? (DateTime?)null : Cycles.Skip(4).FirstOrDefault().Date
};
return RouteList.ToList();
}
}
It seems to me that you do the select-where-order your cycles once for every Cycle1 to Cycle5.
Besides you have to have do something difficult if the result of FirstOrDefault is null.
My advice would be to use more Selects to optimize your code.
In smaller steps:
var selectedSitesAndCycles = dbContext.Sites
// keep only those sites that ...
.Where(site => site.YardID == yardId
&& site.SiteTypeID == siteTypeId
&& site.Season.SeasonYear == DateTime.Now.Year)
// order the remaining sites in ascending order by name
.OrderBy(site => site.Community.Name)
// from every ordered site, get some properties and a list of 5 cycles:
.Select(site => new
{
Pin = site.Pin,
Community = site.Community.Name,
Neighbourhood = site.Neighbourhood,
Address = site.StreetAddress,
Area = site.Area,
Notes = site.Notes,
ClosedDates = site.JobCards
.Where(job => job.OperationID == 1)
.OrderByDescending(job => job.ClosedDate.HasValue)
.ThenBy(job => job.ClosedDate)
.Select(job => Date = job.ClosedDate)
.Take(5)
.ToList(),
});
Note the the query is not executed yet. I only take 5 cycles. I didn't use the keyword new when selecting the ClosedDate. Therefore CycleClosedDates is a List<DateTime?>.
In other words: every element of CycleClosedDates is a nullable DateTime. If you take FirstOrDefault, you get either the first nullable DateTime, which might be a DateTime or null, or you get a (DateTime?)null if there are not enough CycleClosedDates.
Let's examine the case where a site has only three JobCards:
JobCard[0] is closed and has ClosedDate 2020-03-10
JobCard[1] is not closed yet. ClosedDate isn (DateTime?)null
JobCard[3] is closed and has ClosedDate 2020-03-20
// There is no JobCard[4] [5]
The result is a List<DateTime?>, of length 3, where element [1] has no value. The result of Skip(1).FirstOrDefault() will be a nullable DateTime without value.
The nice thing is that Skip(4).FirstOrDefault() will also be a nullable DateTime without value, even though there are not 5 JobCards
Let's continue with an extra Select to create your five properties:
.Select(site => new
{
Pin = site.Pin,
Community = site.Community.Name,
Neighbourhood = site.Neighbourhood,
Address = site.StreetAddress,
Area = site.Area,
Notes = site.Notes,
Cycle1 = site.CycleClosedDates.FirstOrDefault(),
Cycle2 = site.CycleClosedDates.Skip(1).FirstOrDefault(),
Cycle3 = site.CycleClosedDates.Skip(2).FirstOrDefault(),
...
})
Note that the CycleClosedDates will be ordered only once. Because CycleClosedDates is already a List<DateTime?> it seems a bit superfluous to create separate properties instead of one list with length five. Consider in your second select
.Select(site => new
{
Pin = site.Pin,
Community = site.Community.Name,
...
CycleClosedDates = new List[]
{
site.CycleClosedDates.FirstOrDefault(),
site.CycleClosedDates.Skip(1).FirstOrDefault(),
site.CycleClosedDates.Skip(2).FirstOrDefault(),
...
},
};
Or after the first select:
// move the selected data to local process
.AsEnumerable()
// 2nd select:
.Select(site => new
{
Pin = site.Pin,
Community = site.Community.Name,
...
CycleClosedDates = site.CycleClosedDates
.Concat(Enumerable.Repeat( (DateTime?)null, 5)
.Take(5)
.ToList();
This way you are certain that your CycleClosedDates has exactly five nullable DateTimes, even if there were no JobCards at all.

stating which conditions have been matched in linq where

I have a linq statement as such
dbContext.Items
.Where(
p =>
(p.Client.Contact != null && p.Client.Contact.Firstname.ToLower().Contains(searchText.ToLower()))
||
(p.Client.Contact != null && p.Client.Contact.Surname.ToLower().Contains(searchText.ToLower()))
||
(p.PolicyNumber != null && p.PolicyNumber.ToLower().Contains(searchText.ToLower()))
||
(
p.PolicyLivesAssureds
.Where(
pl =>
pl.Contact != null && pl.Contact.Firstname.ToLower().Contains(searchText.ToLower())
|| pl.Contact.Surname.ToLower().Contains(searchText.ToLower())
).Count() > 0
)
)
).OrderBy(p => p.IeUtem);
This is actually needed in an autocomplete. What I want to do is being able to know exactly which among my 5 conditions has been matched and display the particular item that has been matched. For example say that PolicyNumber has been matched i want to send only policynumber for that row and for others if name has been matched i want to send only the name for that row.
Is there a way to do this;
This is a bit more of a food for thought answer as it has flaws in it's approach, but I think it does solve your problem:
double[] items = { 1, 2, 3, 4, 5 };
IEnumerable<Tuple<double, int>> results = items.Select(x =>
{
int index = 0;
foreach (var condition in new Func<bool>[]
{
// TODO: Write conditions here.
() => x == 1,
() => x == 2
})
{
if (condition() == true)
return index;
else
index++;
}
return -1;
}).Zip(items, (matchedCondtion, item) => Tuple.Create(item, matchedCondtion))
.Where(x => x.Item2 != -1);
I've used a simple double array as an example of the collection to filter, but it's just an example, you can use anything.
The first select returns an integer for each element in the collection. If there is a condition match, it returns the index of the condition. If there is not match it returns -1.
It does this by enumerating over the Func collection and returning the index of the first true condition (emulating the short circuiting of the || operator). If no conditions match it simply returns -1 after evaluating all conditions.
These results are then zipped back up with the original collection (using a Tuple), mapping each element with the index of its matching condition (or -1).
So the example would return:
{ 1, 0 },
{ 2, 1 },
{ 3, -1 },
{ 4, -1 },
{ 5, -1 }
This result is then simply filtered using Where to remove any entries with -1, leaving you with a collection of elements that matched a condition and the index of the condition that matched (in the form of a Tuple).
So to customize this for your solution, you can remove the example conditions and place whatever number of conditions you want at:
// TODO: Write conditions here.
The question becomes how do you want to know which queries match. For example you could do something like this
class AutoCompleteItem {
String Text {get; set;}
Item Item {get; set;}
}
var firstNames = dbContext.Items.Select(p => new AutoCompleteItem { Name = p.Client.Contract.FirstName, Item = p})
var lastNames = dbContext.Items.Select(p => new AutoCompleteItem { Name = p.Client.Contract.SurName, Item = p})
var result = firstName.Union(lastNames).Where(p => p.Name.Contains(searchText)).OrderBy(a => a.Item.IeUtem);
Now AutcompleteItem is a class that contains the text you want (and possibly any other fields you need, like information which field it was that matched)
The Idea here is the MVVM patttern. You have your model (the items). Now you need to construct a viewModel (AutoCompleteItems) that actual aids you in displaying what you want.

Linq Select Clause w/ Unknown Number of Fields

I have a linq query in which I need to be able to select an variable number of fields from a datatable. I do know all of the fields that could be included, but only two will for sure be in the datatable. I also will know which fields are included in the datatable (it will just be different depending on the user's selections). Right now I set up something like this:
var query = from item in dt.AsEnumerable()
group item by item.Field<string>("ID") into g
select new
{
ID = g.Key, //required
Status = g.Min(i => dostuff(i,"Status")), //not required
Disc = g.Min(i => dostuff(i,"Disc")), //not required
Loc = String.Join<string>(",", from i in g select i.Field<string>("Loc")) //required
};
dostuff(DataRow i,string field)
{
try
{
return i.Field<string>(field);
}
catch
{
return null;
}
}
So dostuff basically is just checking whether or not that field exists in the dataset, and then I would just need to ignore the non-existant fields when working with the query results, which would not be too difficult. However, it seems like there is probably a better way to do this, but I've had a tough time finding anything via Google about using a dynamic select clause.
You could do it with dynamic type (nb, I did not test so this might have typos.):
var query =dt.AsEnumerable().GroupBy(item => item.Field<string>("ID"))
.Select(g => {
dynamic t = new System.Dynamic.ExpandoObject();
if (g.Table.Columns.Any(c => c.ColumnName == "Status"))
t.Status = g.Field<string>("Status");
if (g.Table.Columns.Any(c => c.ColumnName == "Disc"))
t.Disc = g.Field<string>("Disc");
t.ID = g.Key;
t.Loc = String.Join<string>(",",g.Select(i => i.Field<string>("Loc")));
return t;
}

Categories