Order two different list of objects by date - c#

I have 2 Lists each of different objects. Each list contains a date element. What I am trying to do is pull items from each list in sequence and do something.
Object1
{
string description
date updateDate
int value
}
Object2
{
string description
date updateDate
string descritpion2
}
IE
List<object1>
object1.date = 10/1/2017
object1.date = 9/3/2017
List<object2>
object2.date = 10/15/2017
object2.date = 9/1/2017
I want to process these in order so i would do List 2 object 9/1, List 1 object 9/2, List 1 object 9/3, List 2 object 10/5
How can one achieve this?

How about this?
var list1 = new List<Object1>();
var list2 = new List<Object2>();
var newOrderedByDateCollection = list1
.Select(i => new TempClass(i, i.updateDate))
.Concat(list2
.Select(j => new TempClass(j, j.updateDate)))
.OrderBy(tmp => tmp.Date)
.Select(tmp => tmp.OrigItem);
//This could be replaced by a tuple like Tuple<object, DateTime> but I thought this would come across clearer
public class TempClass
{
public TempClass(object origItem, DateTime date)
{
OrigItem = origItem;
Date = date;
}
public object OrigItem { get; set; }
public DateTime Date { get; set; }
}
You now have a ordered list of type object. Which I can't see a way of getting around, So as you iterate through that list, you'll need to cast each object appropriately back by doing a switch and some pattern matching
Edit: for comepleteness here is the tuple version (I think its probably the best way to do it)
var newOrderedByDateCollection = list1
.Select(i => new Tuple<object,DateTime>(i, i.updateDate))
.Concat(list2
.Select(j => new Tuple<object, DateTime>(j, j.updateDate)))
.OrderBy(tmp => tmp.Item2)
.Select(tmp => tmp.Item1);

If you want to keep type safety (avoid object) and don't mind sorting the lists to new lists, you can do a loop with both indexes:
var l1count = l1.Count;
var l2count = l2.Count;
var ocount = l1count + l2count;
var l1o = l1.OrderBy(o => o.updateDate).ToList();
var l2o = l2.OrderBy(o => o.updateDate).ToList();
for (int j1 = 0, j2 = 0; j1 + j2 < ocount;) {
if (j1 < l1count && (l1o[j1].updateDate <= l2o[j2].updateDate || j2 >= l2count)) {
// process l1o[j1]
++j1;
}
else {
// process l2o[j2]
++j2;
}
}

Related

Except on Lists in c#

I count months and years from a given date to the present date
and from this list I have to subtract the months that were returned to me in the sql (linq) query.
I try to use "Except" on the results, but gives me an error in the picture below
var list = _ecpContext.Akceptacje_UnionAll_V
.Where(f => f.ADLogin == user)
.Select(f => new
{
Miesiac= f.Miesiac, //month
Rok= f.Rok // year
})
.ToList();
//-------------------------------------------------------------------
DateTime employmentDate = _ecpContext.Ustawienia.FirstOrDefault(x => x.UstLogin == user).EmploymentDate;
int employmentYear = employmentDate.Year;
int employmentMonth = employmentDate.Month;
DateTime now = DateTime.Now;
int currentYear = now.Year;
int currentMonth = now.Month;
var newList = Array.Empty<object>().Select(x => new { Month = 1, Year = 1 }).ToList();
for (var i = employmentYear; i <= currentYear; i++)
{
for (var x = employmentMonth; x <= currentMonth; x++)
{
newList.Add(new { Month = x, Year = i });
}
}
//-------------------------------------------------------------------
// i try
IEnumerable<DatesOfShortages> listMissingDates = list.Except(newList);
public class DatesOfShortages
{
public int Year { get; set; }
public int Month { get; set; }
}
new error
The Except method is a method which produces the set difference of two sequences so you need to invoke it.
IEnumerable<DatesOfShortages> listMissingDates = newList.Except(list);
You can't have one list A full of anonymous types, and another list B full of Tuples, and run a.Except(b) on them
Make a list of anonymous types instead of tuples:
var newList = Array.Empty<object>().Select(x => new { Month = 1, Year = 1 }).ToList();
for (var i = employmentYear; i <= currentYear; i++)
{
for (var x = employmentMonth; x <= currentMonth; x++)
{
newList.Add(new{ Month = x, Year = i});
}
}
For newList I suppose something like new [] { list.ElementAtOrDefault(-1) }.ToList(); would work too.. Whatever trick you feel like pulling to get a list of ATs!

c# linq add a column to a new list from another single column list

My project is MVC5. I have a table with multiple rows for the same day, I need to get the total of this entry for each day, I use the following:
var days = db.Nutrition.Where(x => x.Day >= fromDate
&& x.Day <= toDate).DistinctBy(x => x.Day).AsEnumerable().ToList();
List<double?> calories = new List<double?>();
foreach (var item in days)
{
calories.Add(days.Where(c => c.Day==item.Day).Select(x => x.Calories).Sum());
}
I get a list containing the totals. Now I need to make a new list that has two columns.
I made the following model:
public class Consumption
{
public virtual double? Calories { get; set; }
public virtual string Name { get; set; }
}
I tried to use the following to generate the new list:
List<Consumption> newList = new List<Consumption>();
var name = new Consumption { Name = "name" };
foreach (var item in calories)
{
newList.Add(name, Calories = (double)item.Value);
}
I get the following error:
The name 'Calories' does not exist in the current context
Edit
Thanks to Stephen's comment:
I just used one line to achieve same result
var results = db.Nutrition.Where(x => x.Day >= fromDate && x.Day <= toDate).GroupBy(l => l.Day)
.Select(cl => new { Name = "name", Calories = cl.Sum(c => c.Calories)}).ToList();
Try with:
List<Consumption> newList = new List<Consumption>();
var name = new Consumption { Name = "name" };
foreach (var item in calories)
{
var cal = new Consumption{ Name = "name", Calories = (double)item.Value });
newList.Add(cal);
}
You received this compiler error
The name 'Calories' does not exist in the current context
because the List<Consumption>.Add(Comsumption item) method on your variable newList only accepts one argument of type Consumption.
Regarding your intentions, and the discussion in your comments with #StephenMuecke, it became clear that your intention is to Sum a property double Calories, and GroupBy by property DateTime Day and then project that into a List<Consumption>.
var dateTimeFormat = "yyyy-dd-MM";
var results = db.Nutrition.Where(x => x.Day >= fromDate && x.Day <= toDate)
.GroupBy(x => x.Day)
.Select(groupedX => new Consumption
{
Name = groupedX.Key.ToString(dateTimeFormat),
Calories = groupedX.Sum(y => y.Calories)
}).ToList();

GROUPBY and SUM on List items using LINQ

I have a List of type DailySummary
public class DailySummary
{
public string AffiliateID { get; set; }
public string TotalCalls { get; set; }
public string Date{ get; set; }
}
with following sample data:
List<DailySummary> DealerTFNDatesTable = new List<DailySummary>();
DealerTFNDatesTable.Add(new DailySummary() { AffiliateID="0", Date = "12/12/2016", TotalCalls = "10"});
DealerTFNDatesTable.Add(new DailySummary() { AffiliateID="0", Date = "12/13/2016", TotalCalls = "74"});
DealerTFNDatesTable.Add(new DailySummary() { AffiliateID="1", Date = "12/22/2016", TotalCalls = "63"});
DealerTFNDatesTable.Add(new DailySummary() { AffiliateID="0", Date = "12/12/2016", TotalCalls = "58"});
Now I want to retrieve Date and TotalCalls grouped by AffiliateID and assign in another list.
for(int i =0; i < DealerTFNDatesTable.Count; i++)
{
List<NewList> newList = new List<NewList>();
newList.Date = //Assign Dintinct dates WHERE AffiliateId = 0
newList.AffiliateID = //AffiliateID=0
newList.TotalCalls= //TotalCalls SUM GROUPBY DATE and AffiliateID = 0
//For Date '12/12/2016' it will be 68, For '12/13/2016' it will be 74 and so on
}
I'm sorry, I'm new to LINQ. Can someone help me or share resources where I can get a hint to achieve this?
This should work for grouping by AffilateID and Date and then getting the sum (though it's weird to store a number as a string for something like this, but whatever floats your boat).
var results = DealerTFNDatesTable
.GroupBy(x => new { x.AffiliateID, x.Date })
.Select(x => new DailySummary {
AffiliateID = x.First().AffiliateID,
Date = x.First().Date,
TotalCalls = x.Sum(y => Convert.ToInt32(y.TotalCalls)).ToString()
});
If you now look at the result, for example with this code, you get exactly the values you wanted:
foreach (var x in results) {
Console.WriteLine($"id = {x.AffiliateID}, date = {x.Date}, totalCalls = {x.TotalCalls}");
}
> id = 0, date = 12/12/2016, totalCalls = 68
> id = 0, date = 12/13/2016, totalCalls = 74
> id = 1, date = 12/22/2016, totalCalls = 63
First off,
Since DealerTFNDatesTable is a variable, you should use camel case. Thus it is dealerTFNDatesTable
Then to complete #andy his answer, as you also want to do a select. You can select it as follows:
var newVariable = from item in dealerTFNDatesTable
group item by new
{
item.Date,
item.AffiliateID,
}
into g
select new
{
Date = g.Key.Date,
Id = g.Key.AffiliateID,
Total = g.Sum(a => a.TotalCalls)
};
This will give you an IEnumerable, of which you can put the relevant parts in a list by doing var otherList = new List<object>(newVariable
.Where(a => a.Total > 0)); or simply add .ToList() after the select if you want the collection as-is.
Note that this is simply another notation than LINQ, the result is the same.
var results = DealerTFNDatesTable.GroupBy(T => new { T.AffiliateID })
Link

Linq To Objects With DateTime Properties

If you have a collection of:
public class TestObj
{
public DateTime Key { get; set; }
public int MyProperty { get; set; }
public int MyProperty2 { get; set; }
}
The collection might have a count of 20,000 TestObj objects. I need to query using linq on the Key (DateTime) from DateTime t1 to DateTime t2.
I also want to insert and delete from the collection.
I see that the SortedList has an Add and a Remove method. Is SortedList efficient way to handle this problem? I am thinking that a List<T> would have to traverse through the entire list to be sure of getting all objects with t1 and t2.
You should not worry too much about performance of lists. 20,000 is nothing. If you have a list with billions of elements, then it might be more of a problem, but even then, optimization causes more problems than it solves.
If you want to make sure it doesn't matter, you can test it yourself:
var l = new SortedList<DateTime, TestObj>();
var key = new DateTime(1995, 1, 1);
for (var i = 0; i < 20000; i++)
{
var o = new TestObj();
o.Key = key;
key = key.AddDays(1);
l.Add(o.Key, o);
}
var sw = new Stopwatch();
var date1 = new DateTime(1995, 5, 5);
var date2 = new DateTime(2010, 5, 5);
sw.Start();
var between = l.Where(x => x.Key >= date1 && x.Key <= date2).ToArray();
Console.WriteLine(between.Count());
sw.Stop();
Console.WriteLine(sw.Elapsed);
Output is:
5480 (number of elements filtered out) and
00:00:00.0142387 (you wouldn't even notice)

Filter List From List Using Linq

I have retrieved list of my specific class with 150 records.
Now, i want only those records which have Licenseid which are in my another int List.
For example My MainList
List<CustomerDocument> _list = GetListByID(CustomerID);
In this list i have column LicenseID,CustomerID,CustomerName,Type,Age e.t.c
And SecontList
List<int> type = new List<int>();
In Int list i add LicenseID one by one dynamically.
Public class CustomerDocument
{
public int LicenseID{get;set;};
public int CustomerID{get;set;};
public string CustomerName{get;set;};
public int Age{get;set;};
}
This is my CustomerDocument class for which i am getting list.
And now suppose, If Int list has three records , then i want those records from my Main List which have these three LicenseID in my Int List using Linq.
_list = ???
List<CustomerDocument> list = new List<CustomerDocument>();
List<Int> types = new List<Int>();
MapSearchSalesEntities datacontext = new MapSearchSalesEntities();
var collection = ddlLicenseType.CheckedItems;
if (collection.Count > 0)
{
foreach (var item in collection)
{
int value = Convert.ToInt32(item.Value);
types .Add(value);
}
}
var query = (from t1 in datacontext.Licenses
select new CustomerDocument
{
LicenseID = t1.LicenseID,
CustomerID = t1.CustomerID,
CustomerName= t1.CustomerName,
Age= t1.Age,
});
list = query.ToList(); ---gives 150 Records
if (types != null && types.Count > 0)
{
list = list.Where(c => types.Contains(c.LicenseID)).ToList(); --- Gives 0 Records
}
The most efficient approach is to use Enumerable.Join:
var documents = from doc in _list
join licenseID in type
on doc.LicenseID equals licenseID
select doc;
if you want to replace the list:
_list = documents.ToList();
You could also use Enumerable.Where + List.Contains which is not as efficient but shorter:
_list = _list.Where(d => type.Contains(d.LicenseID)).ToList();
Using the LinQ Where method, this is very easy:
_list = _list.Where(c => type.Contains(c.LicenseID)).ToList();
Here is a linq query
var result = (from cust in _list join id in type on cust.LicenseID equals id select cust).ToArray();

Categories