Group array items by dates - c#

Say I have a list of objects like so
list <type>
---------------
o1: date-23.03
o2: date-23.03
o3: date-24.05
o4: date-25.05
How to make another list that contains inner lists of objects that has the same date? For example:
new list<list<type>>
-----------------------
List<type> innerList1 {o1, o2}
List<type> innerList2 {o3}
List<type> innerList3 {o4}
Possible LINQ solutions would be cool, but an algorithm would be nice too.

Don't use a List<object> but a List<RealClass>
Presuming that it's actually a known type and that it's a DateTime property:
List<List<ClassName>> objectsbyDate = listOfObjects
.GroupBy(x => x.DateTimeProperty.Date)
.Select(g => g.ToList())
.ToList();
If it's actually string property as commented, why is that so? You should fix that. However, if you insist on a string you can still use Enumerable.GroupBy. But what if two objects have different years? You won't even mention it since the year is not stored.
Instead convert a DateTime to string at the very last step if you want to display it.

Grouping by your date-object:
List<object> list = new List<object> {o1,o2,o3,o4};
var result = list.GroupBy(g => g);
foreach(var group in result) {
Console.WriteLine(group.Key);
}

Related

C# Sort List<T> by its property which is string of datetime

I have a List every item of that list is user defined class which has a property which type is string but its content is datetime, like the following;
StringAsDate="23/10/2020 17:12:00"
class Unknown{
...,
StringAsDate,
...
}
list of T list
I want to sort the list descending which is string that container DateTime, i have to preserve the format of list at the end.
I can do that by brute force but im looking for efficient and elegant way.
Thanks.
Based on your comments on the answers you are looking for a reflection based solution.
The reflection part, to get the value of a property based on the name:
x.GetType().GetProperty(propertyName).GetValue(x, null)
Try this:
var propetyName = "StringAsDate";
var listSorted = list.OrderByDescending(x => DateTime.Parse(Convert.ToString(x.GetType().GetProperty(propertyName).GetValue(x, null))));
If the Property is already of type DateTime a cast would be sufficient
var propetyName = "StringAsDate";
var listSorted = list.OrderByDescending(x => (DateTime)x.GetType().GetProperty(propertyName).GetValue(x, null));
List<Uknown> list = example for your list containing the objects.
List<Uknown> sorted = list.OrderByDescending(x => DateTimeParse(x.StringAsDate));
Using LINQ:
List<UnKnown> listSorted = list.OrderByDescending(x=>DateTime.Parse(x.StringAsDate));
You can create a list of object.
List<object> sorted = list.OrderByDescending(x => DateTime.Parse(d.GetType().GetField("StringAsDate").GetValue(d).ToString())));
With this your code will work with any class as long as they have this field.
But in case you have control on the Classes being sent to your collection I would suggest that you implement an interface on all the Classes ITimeStamp
and then
List<ITimeStamp> sorted = list.OrderByDescending(x => DateTime.Parse(x.StringAsDate));

Return only values contained in list C#

I have a List<int> ListOfIDs containing some numbers which are IDs.
I have a List<CustomClass> ListOfObjects containing some objects, which properties reflecting their IDs.
I've searched high and low for a Linq query that will allow me to return from my List a sublist of only those objects which have an ID that is contained within the List.
My attempt does not compile and I cannot seem to correct the syntax :
List<CustomClass> SubList = ListOfObjects.Where(ListOfIDs.Contains(p => p.ID))
Thanks very much.
I think you want to do like this?
List<CustomClass> SubList = ListOfObjects
.Where(obj => ListOfIDs.Contains(obj.ID))
.ToList();
I think this is what you need:
List<CustomClass> SubList = ListOfObjects.Where(p => ListOfIDs.Contains(p.ID)).ToList();
Don't forget to call ToList() in the end.
Also consider using HashSet for ListOfIDs, because complexity of Contains operation is just O(1). But, well it depends on how much data you have.
Here's the correct syntax for what you're trying to do:
... ListOfObjects.Where(p => ListOfIDs.Contains(p.ID)).ToList();
Though this might be faster that the Where(Contains) method:
var sublist = (
from obj in ListOfObjects
join id in ListOfIDs on id equals obj.ID
select obj ).ToList();
Try to use this piece of code snippet:
List<CustomClass> SubList = ListOfObjects.Where(o => ListOfIDs.Contains(o.ID))
.ToList();

Group by a distinct list of integers

MyObject()
{
String dept;
List<int> id;
Object obj;
}
Using LINQ, how can I return a list of the above objects organized as follows:
Group all of the obj objects by [ department and EQUAL id list ]. The list being considered equal if it contains the same numbers, not necessarily the same order(a set).
GroupBy has an overload that accepts a custom IEqualityComparer<MyObject>. Write one that regards two objects as equal when dept is equal and id is set-equal, and pass it as an argument.
A convenient way to implement set equality is to write
new HashSet(x.id).SetEquals(new HashSet(y.id))
although this will end up being inefficient and probably not the best idea if there are lots of comparisons to make.
Building off of Jon's answer, if efficiency is an issue, you can store the HashSet for each object in an anonymous object:
myObjects.Select(x => new { myObject = x, hashSet = new HashSet(x.id) })
.GroupBy(x => x.hashSet, HashSet<int>.CreateSetComparer())
.SelectMany(x => x.GroupBy(y => y.myObject.dept))
If you want to perform only one GroupBy you could store the HashSet in a Tuple or custom class, but then you would have to create your own IEqualityComparer.

Linq Intersect with arrays

Im getting a table Tags from the db.
the table has columns ID and TagName
I'm doing something like this to get a list of strings:
var taglist = Model.Tags.Select(x => x.TagName.ToLower()).ToArray();
then I'm comparing against another string array to get the strings that occur in both:
var intersectList = tagList.Intersect(anotherList);
I have my list, but now I also want the ID of each item remaining in the intersect list that corresponds to the tagList. (can just be an int array)
Can anyone help with a good way to do this?
Don't use intersect, it only works for collections of the same type. You could do a simple join or other form of filtering. It would be easiest to throw the string list into a HashSet and filter by tags that contain TagNames in that set. This way, you keep your tags unprojected so they keep their ids and other properties.
var stringSet = anotherList.ToHashSet(StringComparer.OrdinalIgnoreCase);
var tagList = Model.Tags.Where(t => stringSet.Contains(t.TagName)).ToList();
And put them into a list. Don't throw them into an array unless you specifically need an array (for use in a method that expects an array).
Could you do:
var intersectIds = Model.Tags
.Where(tag => anotherList.Contains(tag.TagName))
.Select(tag => tag.Id)
.ToList();
Maybe use Dictionary<int, string> instead of Array?

Sorting Lists with sub items in C#

I'm trying to sort a list of orders and items based on a the earliest (lowest) creation date of one of the items in the list.
So I have:
public MyOrder
{
orderid int;
IList<MyItems> orderitems;
}
public MyItems
{
DateTime itemcreatedate;
}
Say Order1 has two items in it with itemcreatedate 6/1/2010 and 6/15/2010
Order2 has two items in it with itemcreatedate 4/1/2010 and 6/10/2010
I'd like my sorted list to then be Order2, Order1
My meager unfrozen caveman developer brain can see a brute force iterative way to make it happen, but I'm wondering if anyone has a nice clean way.
Try something like this:
List<MyOrder> sortedList = myOrders
.OrderBy(myOrder => myOrder.OrderItems.Min(myItem => myItem.ItemCreateDate))
.ToList();
Here is my (untested!) code :
List<MyOrder> orders = GetSomeOrders();
var orderCreateDateMap = orders.ToLookup(order => order.orderitems.Min(o2 => o2.itemcreatedate));
var sortedGroups = orderCreateDateMap.OrderBy(g => g.Key);
var sortedOrders = sortedGroups.SelectMany(g => g);
The concept is somewhat similar to Mark's one, but I use lookup to avoid IEnumerable<>.Min method to be called multiple times.

Categories

Resources