I have a list (specifically IEnumerable) of items of a specific class:
internal class MyItem
{
public MyItem(DateTime timestamp, string code)
{
Timestamp= timestamp;
Code = code;
}
public DateTime Timestamp { get; private set; }
public string Code { get; private set; }
}
Within this list, there will be multiple items with the same code. Each will have a timestamp, which may or may not be unique.
I'm attempting to retrieve a dictionary of MyItem's (Dictionary<string, MyItem>) where the key is the code associated with the item.
public Dictionary<string, MyItem> GetLatestCodes(IEnumerable<MyItem> items, DateTime latestAllowableTimestamp)
Given this signature, how would I retrieve the MyItem with a timestamp closest to, but not after latestAllowableTimestamp for each code?
For example, given the following for input:
IEnumerable<MyItem> items = new List<MyItem>{
new MyItem(DateTime.Parse("1/1/2014"), "1"),
new MyItem(DateTime.Parse("1/2/2014"), "2"),
new MyItem(DateTime.Parse("1/3/2014"), "1"),
new MyItem(DateTime.Parse("1/4/2014"), "1"),
new MyItem(DateTime.Parse("1/4/2014"), "2")};
If the latestAllowableTimestamp is 1/3/2014, the result would contain only the following items:
Timestamp | Code
----------------
1/3/2014 | 1
1/2/2014 | 2
I can manage to filter the list down to only those timestamps prior to latestAllowableTimestamp, but I don't know linq well enough to pick the most recent for each code and insert it into a dictionary.
var output = items.Where(t => (t.Timestamp <= latestAllowableTimestamp)).GroupBy(t => t.Code);
At this point, I've ended up with two groups, but don't know how to select a single item across each group.
Here is the actual method you are trying to write. It even returns a dictionary and everything:
static Dictionary<string, MyItem> GetLatestCodes(
IEnumerable<MyItem> items, DateTime latestAllowableTimestamp)
{
return items
.Where(item => item.TimeStamp <= latestAllowableTimestamp)
.GroupBy(item => item.Code)
.Select(group => group
.OrderByDescending(item => item.TimeStamp)
.First())
.ToDictionary(item => item.Code);
}
See Enumerable.ToDictionary
This is the your part you should have posted in your question (as LB pointed out)
var list = new List<MyItem>()
{
new MyItem(){ code = "1" , timestamp = new DateTime(2014,1,1)},
new MyItem(){ code = "2" , timestamp = new DateTime(2014,1,2)},
new MyItem(){ code = "1" , timestamp = new DateTime(2014,1,3)},
new MyItem(){ code = "1" , timestamp = new DateTime(2014,1,4)},
new MyItem(){ code = "2" , timestamp = new DateTime(2014,1,4)}
};
DateTime latestAllowableTimestamp = new DateTime(2014, 1, 3);
This is my answer
var result = list.GroupBy(x => x.code)
.Select(x => x.OrderByDescending(y => y.timestamp)
.FirstOrDefault(z => z.timestamp <= latestAllowableTimestamp))
.ToList();
To create your Dictionary, could construct your query like so:
var newDict = items.Where(a => a.Timestamp <= latestAllowableTimestamp)
.GroupBy(b => b.Timestamp)
.ToDictionary(c => c.First().Timestamp, c => c.First());
This should create a Dictionary from your data, with no duplicate days. Note that without the GroupBy query, you'll raise an exception, because ToDictionary doesn't filter out keys it's already seen.
And then if you wanted to get only one MyItem for any given code number, you could use this query:
newDict.Select(a => a.Value)
.OrderByDescending(b => b.Timestamp)
.GroupBy(c => c.Code)
.Select(d => d.First());
The FirstOrDefault query will return only one element from each group. This will give you the MyItem closest to the latest date for any given code.
Related
I have a list that get from database using entity framework.
var list = context.Items;
list result is like this.
var list = new List<Item>{
new Item { id=1, operation="write", date="23.03.2018 08:25:45" },
new Item { id=1, operation="read", date="23.03.2018 09:40:15" },
new Item { id=1, operation="read", date="23.03.2018 10:15:17" },
new Item { id=1, operation="read", date="23.03.2018 11:46:39" }
}
I want to minify this list by operation and last date.
var min = new List<Item>{
new Item { id=1, operation="write", date="23.03.2018 08:25:45" },
new Item { id=1, operation="read", date="23.03.2018 11:46:39" }
}
I am getting last 'write' operation and last 'read' item.
I have used the concept of numbering the rows based on the date, and then filtering them. Something like:
var groups = list.GroupBy(x => x.operation)
.SelectMany(g =>
g.OrderByDescending(i => i.date).Select((j, i) => new { j.id, j.operation,j.date, rn = i + 1 })
);
var data = groups.Where(i => i.rn == 1).ToList();
You can achieve it with the help of following code
var result = list.GroupBy(x => x.operation)
.Select(x => x.OrderByDescending(y => Convert.ToDateTime(y.date)).FirstOrDefault())
.ToList();
Seems like you need distinct value form the list and then you need to get first item with latest date
List<Item> list = context.Items
.GroupBy(a => a.operation)
.Select(g => g.OrderByDescending(p => Convert.ToDateTime(p.date)).First())
.ToList();
you need to perform group by on operation once its done you should take first item from each group but to take latest by date you need to perform descending on date which will make latest date record first recoed and than you just need to apply first function on it.
You can try the following. Just group by with operation and select the last date in each group.
var lastItems = list.GroupBy(p => p.operation).Select(grp => new
{
grp.Key,
lastOperation = grp.OrderByDescending(p => Convert.ToDateTime(p.date)).First()
}).Select(p=> p.lastOperation);
However, if your date field contains different time formats it may fail to convert to DateTime.
And you need to make sure to convert date field to DateTime otherwise you may get different ordering by string.
Edit: The following does the same thing with less typing:
var lastItems = list.GroupBy(p => p.operation).Select(grp =>
grp.OrderByDescending(p => Convert.ToDateTime(p.date)).First());
Since Max is (much) faster than sorting, use Max. Unfortunately your date field is not a DateTime, so conversion is needed. I did the conversion first so I wouldn't have to do it twice when filtering. I also didn't assume there would be only one most recent date per operation, if you need only you could add a First. I preserved and returned the original Item at the end.
var min = from item in list
select new { item, date = DateTime.Parse(item.date) } into newitem
group newitem by newitem.item.operation into itemgroup
let maxdate = itemgroup.Max(i => i.date)
from item in itemgroup
where item.date == maxdate
select item.item;
Note: It is possible that .Net Core handles the case of OrderBy.First specially without sorting.
I'm trying to filter the results I'm getting by removing some of the items I have in my custom dictionary by their value. So if there are multiple items with the same value I would like to have only one sample of that pair.
This is the custom class I have where I'm storing the values:
public class ValuePair
{
public string Text { get; set; }
public string Value { get; set; }
}
Here is how I'm retrieving the values:
List<ValuePair> items = GetResults(db)
.AsEnumerable()
.Distinct()
.Select(v => new TextValuePair
{
Text = ToTitleCase(v.NameOfTown),
Value = v.NameOfTown
})
.ToList();
I would like to know how I can refresh the results and get only one sample of the items filtered by the value, not by the key.
How can I do that?
You can group by Value then take the first item of the grouped items.
List<ValuePair> items = GetResults(db)
.AsEnumerable()
.Distinct()
.Select(v => new TextValuePair
{
Text = ToTitleCase(v.NameOfTown),
Value = v.NameOfTown
})
.GroupBy(x => x.Value)
.Where(x => x.Key == "filter") // filter by Value (the prop name is Key)
.Select(x => x.First())
.ToList();
you can use this https://code.google.com/p/morelinq/ librarry it has an extension method called
DistictBy and then you can
List<ValuePair> items = GetResults(db)
.AsEnumerable()
.Select(v => new TextValuePair
{
Text = ToTitleCase(v.NameOfTown),
Value = v.NameOfTown
}).DisrinctBy(c=>c.Value)
.ToList();
I have a class and its List
abc cs = new abc();
List<abc> Lst_CS = new List<abc>();
and I set some value by HidenField in foreach loop
foreach (blah blah)
{
cs = new abc{
No = VKNT,
GuidID=hdnGuidID.Value.ToString(),
RecID=hdnRecID.Value.ToString(),
Date=HdnDate.Value.ToString()
};
Lst_CS.Add(cs);
}
and finally I get a List_CS and I order by Lst_CS according to Date like this;
IEnumerable<abc> query = Lst_CS.OrderBy(l => l.Date).ToList();
but in extra, I want to group by according to No.
Briefly, I want to order by Date and then group by No on Lst_CS How can I do ?
Thanks for your answer
Well you just just do the ordering then the grouping like so:
Lst_CS.OrderBy(l => l.Date)
.GroupBy(l => l.No)
.ToList();
Each list of items in each group will be ordered by date. The groupings will be in the order that they are found when the entire list is ordered by date.
Also your ForEach can be done in one Linq statement, then combined with the ordering and grouping:
var query = blah.Select(b => new abc{
No = VKNT,
GuidID=hdnGuidID.Value.ToString(),
RecID=hdnRecID.Value.ToString(),
Date=HdnDate.Value.ToString()
})
.OrderBy(l => l.Date)
.GroupBy(l => l.No)
.ToList();
I have a list Having multiple Items and 3 props ID,DATE,COMMENT.ID field is Auto incremented in DATABASE.
Let say list Contains
2,16AUG,CommentMODIFIED
1,15AUG,CommentFIRST
3,18AUG,CommentLASTModified
I want to get a single ITEM.Item Having Minimum DATE and having Latest Comment. In this case
1,15AUG,CommentLASTModified
Any easy way to do it using LINQ.
orderedItems = items.OrderBy(x => x.Date);
var result = items.First();
result.Comment = items.Last().Comment;
To get a single item out of the list, you can order the items then take the first one, like this:
var result = items
.OrderByDescending(x => x.Date)
.First();
But First will throw an exception if the items collection is empty. This is a bit safer:
var result = items
.OrderByDescending(x => x.Date)
.FirstOrDefault();
To get the min / max of different columns you can do this:
var result =
new Item {
Id = 1,
Date = items.Min(x => x.Date),
Comment = items.Max(x => x.Comment)
};
But this will require two trips to the database. This might be a bit more efficient:
var result =
(from x in items
group x by 1 into g
select new Item {
Id = 1,
Date = g.Min(g => g.Date),
Comment = g.Max(g => g.Comment)
})
.First();
Or in fluent syntax:
var result = items
.GroupBy(x => 1)
.Select(g => new Item {
Id = 1,
Date = g.Min(g => g.Date),
Comment = g.Max(g => g.Comment)
})
.First();
I am currently using Linq to retrieve a list of distinct values from my data table. I am then looping through the list and again calling a linq query to retrieve a list of values for each
value in the first list.
_keyList = new SortedList<int, List<int>>();
var AUGroupList = ProcessSummaryData.AsEnumerable()
.Select(x => x.Field<int>("AUGroupID"))
.Distinct()
.ToList<int>();
foreach (var au in AUGroupList)
{
var AUList = ProcessSummaryData.AsEnumerable()
.Where(x => x.Field<int>("AUGroupID") == au)
.Select(x => x.Field<int>("ActivityUnitID"))
.ToList<int>();
_keyList.Add(au, AUList);
}
I am then adding the value to a sorted list along with the corresponding second list.
How can I combine the above two queries into one Linq query so that I don't have to call them separately?
You should be able to do something like:
var groupQuery = from d in ProcessSummary.AsEnumerable()
group d by new { Key = d.Field<int>("AUGroupID") } into g
select new { GroupID = g.Key, Values = g.Distinct().ToList() };
Then you can loop through the groupQuery and populate the sorted list. The Key property will contain the group id, and the Values property will have a distinct list of values.
Have you tried this?
var _keyList = new SortedList<int, List<int>>();
var AUGroupList = ProcessSummaryData.AsEnumerable()
.Select(x => x.Field<int>("AUGroupID"))
.Distinct()
.Where(x => x.Field<int>("AUGroupID") == au)
.Select(x => x.Field<int>("ActivityUnitID"))
.ToList<int>();
_keyList.Add(au, AUList);
}
Your provider should cope with that, if not there's a few other ways.