OrderByDescending - specific data 1st and then rest in order - c#

We have data as follows
Created On
Supplier
01-Nov-21
ABC
02-Nov-21
XYZ
15-Nov-21
ABC
20-Nov-21
MNO
25-Nov-21
ABC
So we want to record in descending order of creation but 1st want all "ABC" records on the top.
I tried with this one, but didn't worked.
object.OrderByDescending(m => m.name== "ABC").ThenBy(x => x.CreatedOn)
So instead of getting records as follows
01-Nov-21 | ABC
15-Nov-21 | ABC
25-Nov-21 | ABC
02-Nov-21 | XYZ
20-Nov-21 | MNO
It was coming in same order as above i.e. based on createdOn field
Am i missing anything here?

in descending order of creation but 1st want all "ABC" records on the top
You need to use ThenByDescending - using OrderByDescending initially doesn't then set the sorting of the date up to be descending too
I don't personally think you should sort a bool in your first clause, because it's not obvious to everyone how booleans sort. Rather than it be something one has to remember/look up, I would recommend that you use the bool to generate an int for sorting because everyone can easily understand your intent (everyone can put 0 and 1 into ascending order):
foreach(var x in list.OrderBy(m => m.name == "ABC" ? 0 : 1).ThenByDescending(x => x.CreatedOn))
//process your objects in turn here
To achieve a "date descending but all ABC on top" you first sort on whether something is ABC or not (and bear in mind that that's an exact, case sensitive match, unless this query is being translated to SQL and send to a DB like SQL server, where case sensitivity depends on the collation of the column being sorted/where trailing spaces may be ignored), then you want to sort the date descending with ThenByDescending.

Related

I need a sort function that sorts within the sort function

I am trying to create a sort function that intelligently sorts our embroidery design schedule. Orders have Id's and might have 2 or 3 designs per order. Each list entry corresponds to one design. Designs with the same name may also appear across multiple orders, so design 'Manchester Utd Crest' may appear on order 123456 and order 55555
I want to sort by the design names first (string) and then by OrderId's (int). So I want all my design names (e.g. Manchester Utd Crest) to stay together on our schedule. However, if there is another design on that same order, I want the second design in the order to appear below the first design, even though it has a different name.
So I would end up with something like this:
Here is my code so far: it only sorts by OrderId, then by Design Name. I need it to sort by Design Names, but also make sure OrderIds that are the same always stay together in the list as well, which it doesn't do right now:
embroideryJobs.Sort((EmbroideryJob a, EmbroideryJob b) =>
{
if (a.OrderId != b.OrderId)
return a.OrderId.CompareTo(b.OrderId);
if (a.DesignProcessData[0].Name != b.DesignProcessData[0].Name)
return a.DesignProcessData[0].Name.CompareTo(b.DesignProcessData[0].Name);
return 0;
});
It sounds like you're wanting to sort by more than one criteria. You can do that with the LINQ functions OrderBy and ThenBy. So, you would want to sort like this:
var sortedJobs = embroideryJobs.OrderBy(x => x.OrderId).ThenBy(x => x.DesignName);
If embroideryJobs contained this:
OrderId DesignName
123456 Manchester Utd Crest
987654 Manchester Utd Crest
123456 Name & Number
5555555 Name & Number
5555555 Oldham Athletic Crest
sortedJobs would contain this:
OrderId DesignName
123456 Manchester Utd Crest
123456 Name & Number
987654 Manchester Utd Crest
5555555 Name & Number
5555555 Oldham Athletic Crest
Also, be sure you having a using statement that pulls in the System.Linq namespace:
using System.Linq;
at the top of the file.

Get row that has some specific conditions

I've got 2 tables
Table 1 has a detail of progress of assignment in table 2
Table 1. Assignment
ID | Class | Content | StudentID | LastReviewDate
..............
3133 GC ABCD 1059 4/6/2018 17:35
Table 2. Detail
MondayOfWeekDate here is begin day of the week
AssignmentID | MondayOfWeekDate | Percent | StudentID | LastReviewDate
3133 3/19/2018 0 1059 3/23/2018 17:20
3133 4/2/2018 100 1689 4/7/2018 10:35
..............
I tried to write SQL to indicate between 3/26/2018 to 4/1/2018 to show assignment 3133 in that week but failed
SELECT Assignment.* FROM Assignment,Detail
WHERE Assignment.Class = 'GC'
AND Assignment.ID=Detail.AssignmentID
AND Detail.MondayOfWeekDate >'2018/03/26'
AND Percent<100
Of course the result was nothing but i can't find the right code to execute
Can you please me how to write SQL to show that information properly ? Thank you a lot.
SELECT Assignment.* FROM Assignment,Detail
WHERE Assignment.Class = 'GC'
AND Assignment.ID=Detail.AssignmentID
AND Detail.MondayOfWeekDate BETWEEN '2018-03-26' and '2018-04-01'
AND Percent<100
You will get no results as there is nothing between 3/26/2018 to 4/1/2018??
The only mistake you made is your date which is to use between and its a better idea to use iso formatted date yyyy-MM-dd
I cannot directly speak to why you don't see anything. But I can suggest how to write a more readable and maintainable query. This might fix your problems but I don't know.
Some simple guidelines:
Never use commas in the FROM clause.
Always use proper, explicit, standard JOIN syntax.
Use table aliases.
Use standard date formats.
So, start with a query like this:
SELECT a.*
FROM Assignment a JOIN
Detail d
ON a.ID = d.AssignmentID
WHERE a.Class = 'GC' AND
d.MondayOfWeekDate > '2018-03-26' AND
d.Percent < 100;
Also check for syntax errors when you run the query. It can be easy to make small errors that cause the query not to run.
Your query fails because the row
3133 4/2/2018 100 1689 4/7/2018 10:35
Is the one bigger than >'2018/03/26' but its Percent is not <100, but 100.
What are you exactly looking for?

Compare 2 DataTables and Select Rows Not in 2nd table Using 2 Criteria

I need this function for a transaction table. My datatables looks like this.
RecordsInDatabase-Table
a_code | b_code
AB | 001
AB | 002
AC | 001
RecordsInTextFile-Table
a_code | b_code
AB | 002
AC | 005
AC | 009
I need to compare using two IDs, a_code and b_code.
Therefore, if I run the LINQ code (or other), a datable would contain records that are IN the database, but NOT in the text file.
RecordsNotInTextFile-Table
a_code | b_code
AB | 001
AC | 001
I already have a LINQ code, but it only compares using one ID.
DataTable affixesInDatabase = affixDAO.SelectAllAffix();
IEnumerable<string> affixesNotInTextFile = affixesInDatabase.AsEnumerable().Select(r => r.Field<string>("affix_code"))
.Except(affixesInTextFile.AsEnumerable().Select(r => r.Field<string>("affix_code")));
if (affixesNotInTextFile.Any())
{
DataTable affixesToBeDeleted = (from row in affixesInDatabase.AsEnumerable()
join id in affixesNotInTextFile
on row.Field<string>("affix_code") equals id
select row).CopyToDataTable();
foreach (DataRow dr in affixesToBeDeleted.Rows)
{
affixDAO.DeleteAffix(dr[0].ToString());
}
}
return "Affix data successfully edited.";
If you need to filter out two exceptions, the simplest way to do it would be to chain two .Except() calls into your LINQ expression.
From your comment, it appears that you aren't familiar with the principles on which LINQ operates. If you want to understand, I'd suggest reading Jon Skeet's "Edulinq" series, in which he re-implements the standard LINQ query operators and explains how it all works as he goes. But here's the short version:
LINQ is a set of extension methods that operate on enumerable sequences (ie. IEnumerable<T>). Each method takes a sequence as input and applies some operation, and the majority of them produce a new sequence as output. (There are some that produce a single value instead, but not the ones we're dealing with.) So here's what your query is doing:
IEnumerable<string> affixesNotInTextFile = //assignment. You know how that works
affixesInDatabase //our base sequence that we're starting from
.AsEnumerable() //ensure that we're working with an IEnumerable, not an IQueryable
.Select() //transform the sequence from the original objects to a sequence of Field<string> values from the original objects
.Except() //filter out of the transformed sequence anything that's in *this* sequence
Except() produces a new sequence, and so you can chain another .Except() on the end of the expression that will filter the sequence further. Read the Edulinq posts, and pay particular attention to the ones on Select and Except, and you should come out of it understanding how LINQ works well enough to accomplish what you need.

Ordering a List by the highest value of two properties

I have a list like the following one:
|Name|Val. Date 1| Val. Date 2|
|PA | 200| 200|
|PB | 0| 300|
|PC | 200| 0|
|PD | 150| 150|
Ordering the list by Val. Date1 will give me something like: PA, PC, PD and PB. and something similar if I do it for Val. Date 2.
But what I really need to do, is to order it by the highest value recorded in Date 1 or Date 2, what results in something like this: PB, PA, PC and PD.
How can I solve this matter?
use Math.Max() to get the maximum value and then order by it:
list.OrderByDescending(x => Math.Max(x.Date1, x.Date2));
Since you are using a list, you can do an inline sort of the list using List.Sort(Comparison<T>):
list.Sort((lhs, rhs) =>
(
Math.Max(lhs.Val.Date1, lhs.Val.Date2) -
Math.Max(rhs.Val.Date1, rhs.Val.Date2)
));
If you are assigning the results of the Linq solution back to a list using .ToList(), then doing an in-place sort instead will be much more efficient with one important caveat:
List.Sort() is NOT a stable sort, so that it doesn't preserve the ordering of equal values. The Linq OrderBy, however, is a stable sort.
Another way is to use something like:
var sortedList = ListOfData.OrderByDescending(x=>x.Val1).ThenByDescending(x=>x.Val2);
See:
Multiple “order by” in LINQ
Ordering data in LINQ queries by more than one column
Multiple Field Sorting by Field Names Using Linq

Weird behavior in Entity Framework Linq in string EndsWith method

Background
I have a table that contains only one column: Name.
There are only four rows in it, say
| Name |
| test1.com |
| test2.com |
| test3.com |
| test4.com |
Problem
If I query
var email = "a#test2.com";
Table.Where(x => email.EndsWith(x.Name));
I'll get an empty list. but If I query all rows first and calculate Where in memory like this
var email = "a#test2.com";
Table.ToList().Where(x => email.EndsWith(x.Name));
I'll get a list contains only test2.com which is correct.
The generated SQL for the first query is
SELECT "Extent1"."Name" AS "Name"
FROM "USER"."Table" "Extent1"
WHERE (( NVL(INSTR(REVERSE(:p__linq__0), REVERSE("Extent1"."Name")), 0) ) = 1)
I've tried replacing :p__linq__0 with 'a#test2.com' and running the query in the SQLDeveloper, the result is correct.
More Information
If I change EndsWith() to Contains(), the problem will be gone. Here is the generated SQL for Contains()
SELECT "Extent1"."Name" AS "Name"
FROM "USER"."Table" "Extent1"
WHERE (( NVL(INSTR(:p__linq__0, "Extent1"."Name"), 0) ) > 0)
Do you have any idea what's wrong with EndsWith or REVERSE method?
Environment
EF5.0
.NET4.5
Oracle11g
ODP.NET11.2 Release 3
This line concerns me and is a common pitfall with people using EF:
Table.ToList().Where(x => email.EndsWith(x.Name));
The part Table.ToList() is the worst part because this will actually materialise the entire table into memory and then perform the EndsWith in C#.
This line:
Table.Where(x => email.EndsWith(x.Name));
I would caution this approach just on general principle as it will be horrendously slow when the table grows to reasonable size. You can do the heavy lifting before the query hits the database by splitting out the domain from the the email as you construct the query:
var email = "a#test2.com";
/* You should null check this of course and not just assume a match was found */
var domain = Regex.Match(email , "#(.*)").Groups[1].Value;
/* Note: ToList() materialisation happens at the end */
var result = Table.Where(x => x.Name == domain).ToList();
Furthermore, if you need to match on the domain names of a column storing emails, then my preferred approach would be to split the email and store the domain name in a separate column that you index and just match on, this will scale and be a heck of a lot easier to manage. Remember that these days data is cheap... especially compared to non-indexable table scans.
Also remember (for both scenarios) that your database is set to CI (case insensitive)

Categories