How to Select then OrderBy using LINQ? - c#

I am trying to sort the contents of a CSV file according to their dates with the following code from a question a posted earlier:
private class CSVEntry
{
public DateTime Date { get; set; }
public string Grp { get; set; }
}
...
List<CSVEntry> csvList = new List<CSVEntry>();
csvList.Add(new CSVEntry() { Date = DateTime.ParseExact(col[7], "dd/MM/yyyy", null), Grp = col[9] });
var results = csvList.OrderBy(x => x.Date); // An error occurs at "Date".
...
But I got this error:
'string' does not contain a definition for 'Date' and no extension method 'Date' accepting a first argument of type 'string' could be found.
What I want the output to be is the strings from other columns but sorted chronologically. When I tried using this code to display the dates:
var results = csvList.Where(x => x.Grp == "DEFAULT").OrderBy(x => x.Date);
It works perfectly with the output as dates sorted chronologically. However, this time I don't want to display the dates. I want to display the strings from other columns like I've mentioned above. I tried using
var results = csvList.Select(x => x.Grp).OrderBy(x => x.Date);
but got the same error as well. Where have I gone wrong? I am new to LINQ and unfamiliar with List<T> and IEnumerable<T> and this is my first time using them.

Add Select after your order the records, Like below
var results = csvList
.Where(x => x.Grp == "DEFAULT")
.OrderBy(x => x.Date)
.Select(x => x.Grp);

Reddy's answer should work, but I will explain it for you, so you understand why it does.
When you use the Select(x => x.Grp) Statement you don't have a IEnumerable<CSVEntry> anymore. You only have a IEnumerable with all Grp-Entrys from your csvList. So When you try to order them by Date by adding a OrderBy(x => x.Date) Statement, Linq doesn't know the Date Property, because in this Statement x is only a string, not a CsvEntry.
In Reddys Answer, he first filter all Entrys by Grp == "DEFAULT". After that he has a IEnumerable<CsvEntry> left, so he can Order them by x.Date. After that, again, he has a IEnumerable<CsvEntry> left. Only after the last Statement, the Select(x => x.Grp) Statement, he has a IEnumerable<string> left.
Hope that helps :)

Related

Convert to IEnumerable<DateTime> result of LINQ Query

I have a linq Query
var tListOfDates = tList.GroupBy(g => g.dateOfSlot)
.Select(s => new {s.Key.Value });
dateOfSlot is a DateTime value
How can I convert tListOfDates to IEnumerable<DateTime>
I've tried top cast the result, but it doesn't work.
You should drop anonymous class new {s.Key.Value } (you don't want it but DateTime):
var tListOfDates = tList
.GroupBy(g => g.dateOfSlot)
.Select(s => s.Key.Value);
It seems that dateOfSlot is of type DateTime?, not DateTime since you put s.Key.Value, not s.Key; if my guess is right you can put it as
var tListOfDates = tList
.Where(item => item.dateOfSlot.HasValue)
.Select(item => item.dateOfSlot.Value)
.Distinct();
A proposition,you take all dates and get the unique date by Distinct
var tListOfDates = tList.Select(g => g.dateOfSlot).Distinct();
I Solved with
IEnumerable<DateTime> tListOfDates = tList.Where(w => w.dateOfSlot.HasValue).Select(g => g.dateOfSlot).Distinct().ToArray().Cast<DateTime>();
May be there is something more that needed and the query can be simplified.
So you have an object tlist, which implements IEnumerable<MySlot>. We don't know a lot of MySlot, yet we know that every MySlot object has a DateTime property DateOfSlot.
The first part of your LINQ statement groups all MySlots in your tlist into groups of MySlots where every MySlot in the group has equal DateOfSlot:
tList.GroupBy(mySlot => mySlot.dateOfSlot)
Every group has a Key which contains this common DateOfSlot. So the Key is d DateTime object
The second part projects every group into one element:
.Select(group => new {group.Key.Value });
group.Key is a DateTime. The problem is that a Datetime does not have a property Value. Are you sure that DateOfSlot is a DateTime?
It's not entirely certain what you want.
I have an IEnumerable<MySlot> in tList, and I want all used DateOfSlot values in this list
var allUsedDateOfSlotValues = tList.Select(mySlot => mySlot.DateOfSlot);
But now I have duplicates, I don't want duplicates!
var allDistinctUsedDateOfSlotValues = tList
.Select(mySlot => mySlot.DateOfSlot)
.Distinct();
This will have the same result as your code:
var result = tList.GroupBy(mySlot => mySlot.DateOfSlot)
// result: groups of mySlots with same DateOfSlot
.Select(group => group.Key)
// result: distinct values of DateOfSlot

Group By struct list on multiple columns in C#

I am having a struct as
public struct structMailJob
{
public string ID;
public string MailID;
public int ResendCount;
public int PageCount;
}
and a list as
List<structMailJob> myStructList = new List<structMailJob>();
I have loaded data in myStructList from database and want myStructList data in a new list after grouping by MailID and ResendCount.
I am trying as:
List<structMailJob> newStructList = new List<structMailJob>();
newStructList = myStructList.GroupBy(u => u.MailID, u=>u.ResendCount)
.Select(grp => new { myStructList = grp.ToList() })
.ToList();
but unable to do that as getting error message - cant implicitly convert generic list to structMailJob.
I think that you are looking for is the following:
var newStructList = myStructList.GroupBy(smj => new { smj.MailID, smj.ResendCount })
.Select(grp => new
{
MailID = grp.Key.MailID,
ResendCount = grp.Key.ResendCount
MailJobs = grp.Select(x=>new
{
x.ID,
x.PageCount
}).ToList()
})
.ToList();
Note that we changed the GroupBy clause to the following one:
GroupBy(smj => new { smj.MailID, smj.ResendCount })
Doing so, the key on which the groups would be created would be consisted of both MailID and ResendCount. By the way the former GroupBy clause isn't correct.
Then having done the grouping, we project each group to an object with three properties, MailID and ResendCout, which are the components of the key and list of anonymous type object with two properties, ID and PageCount, which we gave it the name MailJobs.
Last but not least you will notice that I didn't mention the following
List<structMailJob> newStructList = new List<structMailJob>();
I just used the var and declared the newStructList. I don't think that you stated in your post makes sense. How do we expect to get a list of the same objects after grouping them? So I assumed that you might want is the above.
However, I thought you might want also something like this and you didn't want to refer to Grouping.
myStructList = myStructList.OrderBy(smj => smj.MailID)
.ThenBy(smj => smj.ResendCount)
.ToList();
Linq Query is completely incorrect, following are the important points:
myStructList.GroupBy(u => u.MailID, u=>u.ResendCount) // Incorrect grouping
myStructList.GroupBy(u => new {u.MailID, u.ResendCount }) // Correct grouping, which will do by two columns MailID and ResendCount, last one was only doing by MailID and was using ResendCount for result projection
Now the result is of type IEnumerable<IGrouping<AnonymousType,structMailJob>>, so when you do something like Select, it will end up creating Concatenated List of type IEnumerable<List<structMailJob>> (Removed the assignment to myStructList inside the Select, as that was not correct):
.Select(grp => grp.ToList())
Correct code would require you to flatten using SelectMany as follows:
newStructList = myStructList.GroupBy(u => new {u.MailID, u.ResendCount})
.SelectMany(grp => grp.ToList()).ToList();
Assign it to newStructList, but this code has little use, since literally newStructList is exactly same as myStructList post flattening, ideally you shall be able to use the grouping, so that you can get a subset and thus the correct result, however that depends on your business logic
I don't know if I got your question right but it seems to me you missed the 'Group by' signature.
List<structMailJob> myStructList = new List<structMailJob>();
List<structMailJob> newStructList = new List<structMailJob>();
newStructList = myStructList
// .GroupBy(/*Key Selector */u => u.MailID, /*Element Selector*/u=>u.ResendCount)
.GroupBy(u => new { u.MailID, u.ResendCount }) // broup by MailID, ResendCount
// Note no Element Selector , the 'STRUCT' is 'SELECTED'
.Select(grp => {
// NOte: Key == Anonymous {MailID, ResendCount }
return grp;
})
// otherwise you get a IEnumerable<IEnumerable<T>> instead of IEnumerable<T> because you grouped it
.SelectMany(x=>x)
.ToList();
If Mrinal Kamboj's answer is what you are looking for, then you could use the following as an alternative:
var orderedList = myStructList.OrderBy(x => x.MailID).ThenBy(x => x.ResendCount);

String Format on LINQ controller statement MVC

im having problems using String format on my LINQ but only on my Controller, using it on my view work well, so how can i change my LINQ to not give me an error in the Controller.
This is my LINQ
foreach (var item in db.Pos.Where(r => r.Fecha.Day.ToString() == "2").Select(g => new { Pdv = g.Pdv, Total = g.Total })
.GroupBy(l => l.Pdv).Select(z => new { Pdv = z.Key, Total = String.Format("{0:$#,##0.00;($#,##0.00);Zero}",Decimal.Round(z.Sum(l => l.Total), 0) }))
{
listadepuntos.Add(item.ToString());
}
var grupoPdv = new SelectList(listadepuntos.ToList());
ViewBag.GroupS = grupoPdv;
what i want is that the data of ViewBag.GroupS gets ',' each 3 digits , like for hundreds, thousands and millions right now i get the date plain without format.
what can i do?
You can call AsEnumerable extension method after your group by to execute your Select method using Linq to Objects instead Linq to Entities:
.GroupBy(l => l.Pdv)
.AsEnumerable()// Add this
.Select(z => new { Pdv = z.Key, Total = String.Format("{0:$#,##0.00;($#,##0.00);Zero}",Decimal.Round(z.Sum(l => l.Total), 0) })
The issue is because your Linq provider doesn't know how to convert your method calls to a proper expression tree, which later need to be translated to a SQL statement. There is a few string methods that are currently supported (you will find them in this link), String.Format is not one of them. In case of Decimal.Round which is either supported, you could use System.Math.Round instead.

Linq Select causes InvalidCastException

var courses = db.Courses.Select(course => new Model.Course() {
Dates = db.Dates
.Where(date => date.CourseID == course.ID)
.Select(date => date._Date.ToString())
.ToList()
});
return courses.ToList();
The return call causes System.InvalidCastException. If I remove Dates, there is no error.
Here's Course class:
public class Course {
public List<string> Dates { get; set; }
}
Screenshot from VS:
The .ToString() on your _Date column could be causing the InvalidCastException. Linq to SQL is trying to convert your _Date into a string using ToString() which does not exist in SQL. You will need to simply grab the date as is from SQL and convert to string in memory.
Try this:
// Grab from db list of courses and their dates.
// Put dates in a List of DateTime, but you can also use DateTime2.
// Note that courses will be an IQueryable.
var courses = db.Courses.Select(course => new List<DateTime> {
Dates = db.Dates
.Where(date => date.CourseID == course.ID)
.Select(date => date._Date).ToList()
});
// Extract data from SQL by calling our IQueryable's ToList() of method. This puts the data as a List in memory.
// Using the List.Select() method, convert dates to strings. This will return an IEnumerable.
// Convert the resulting IEnumerable back to a List.
return courses
.ToList()
.Select(course => new Model.Course
{
// Here, you are using the .NET Framework's DateTime.ToString()
// So you can use any formatting options available with that.
Dates = course.Dates.Select(date => date.ToString()).ToList()
})
.ToList();
You didn't show the complete model but I think you should rather be joining those two tables with a join and not a where similar to this:
var courses =
db.Courses
.Join(
db.Dates,
c => c.ID,
d => d.CourseID,
(c, d) => new
{
Course = c,
Dates = d
}
).ToList();

C# - Failed to compare two elements in the array

This is my code:
var distinctDateValues = dt.AsEnumerable()
.Select(row => new
{
Date = DateTime.Parse(row.Field<string>("DAY"))
})
.Distinct()
.ToList();
distinctDateValues.Sort(); // getting error on this line
Values in distinctDateValues are:
The error i am getting is "Failed to compare two elements in the array."
Can anybody suggest me as what i am doing wrong here. I want to sort the values in date column of distinctDateValues.
Needless to create anonymous type, in your case the result distinctDateValues is a list of anonymous type, not a list of DateTime, you should get the sorted list of DateTime like below with OrderBy:
var distinctDateValues = dt.AsEnumerable()
.Select(row => row.Field<DateTime>("DAY"))
.Distinct()
.OrderBy(x => x)
.ToList();
Also, you should use built-in method Field<DateTime> instead of using one more step with DateTime.Parse
Just guessing here... your distinctDateValues don't know how to compare themselves... You would need to implement IComparable or something...
Try this:
var distinctDateValues = dt.AsEnumerable()
.Select(row => DateTime.Parse(row.Field<string>("DAY")))
.Distinct()
.ToList();
distinctDateValues.Sort(); // should not get any errors here...
If you really want to create an anonymous type (e.g., you are only showing us a small part of your code), try this:
var distinctDateValues = dt.AsEnumerable()
.Select(row => new
{
Date = DateTime.Parse(row.Field<string>("DAY"))
})
.Distinct()
.OrderBy(d => d.Date) // do the sorting here with linq
.ToList();

Categories

Resources