I have 2 lists in C#:
public class AvailableSlot
{
public DateTime DateTime;
public string Name
}
List<AvailableSlot> list1 = GetList();
List<AvailableSlot> list2 = GetAnotherList();
I want to call intersect on these lists to find out where there are items in both lists for the same date. I know i can use .Intersect to get this info but I have a slightly more complicated requirement. I want to return a intersected list but i want to this list to contain a list of objects with all of the name in them. so something like this:
List<AvailableSlot2> intersectedList . ..
where AvailableSlot2 is this below:
public class AvailableSlot2
{
public DateTime DateTime;
public string[] Names;
}
Is there any ability to do this transformation after trying to intersect between two lists?
I would just union the two lists, group by DateTime and then pull out the names from the group:
var list1 = new List<AvailableSlot>
{
new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Alpha" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Bravo" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 3), Name = "Charlie" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Delta" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Echo" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 3), Name = "Foxtrot" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 4), Name = "Golf" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 5), Name = "Hotel" }
};
var list2 = new List<AvailableSlot>
{
new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Apple" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Bannana" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 1), Name = "Dog" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 2), Name = "Egg" },
new AvailableSlot { DateTime = new DateTime(2013, 2, 5), Name = "Hi" }
};
var list3 = list1.Where (l => list2.Where (li => l.DateTime == li.DateTime).Any ())
.Union(list2.Where (l => list1.Where (li => l.DateTime == li.DateTime).Any ()));
var groupedItems = from slot in list3
group slot by slot.DateTime into grp
select new AvailableSlot2 {
DateTime = grp.Key,
Names = grp.Select (g => g.Name).ToArray()
};
foreach(var g in groupedItems)
{
Console.WriteLine(g.DateTime);
foreach(var name in g.Names)
Console.WriteLine(name);
Console.WriteLine("---------------------");
}
Output:
2/1/2013 12:00:00 AM
Alpha
Delta
Apple
Dog
---------------------
2/2/2013 12:00:00 AM
Bravo
Echo
Bannana
Egg
---------------------
2/5/2013 12:00:00 AM
Hotel
Hi
---------------------
You can use a LINQ to Objects Join() to line up items with the same DateTime property and then collect all the names into an array
var joinedItems = from slot1 in list1
join slot2 in list2
on slot1.DateTime equals slot2.DateTime into g
where g.Any()
select new AvailableSlot2
{
DateTime = slot1.DateTime,
Names = Enumerable.Range(slot1.Name,1).Union(g.Select(s => s.Name)).ToArray()
}
You can make use of ToLookup:
DateTime dt1 = new DateTime(2013, 2, 1);
DateTime dt2 = new DateTime(2013, 3, 1);
DateTime dt3 = new DateTime(2013, 4, 1);
var list1 = new List<AvailableSlot>
{
new AvailableSlot{DateTime = dt1, Name = "n1",},
new AvailableSlot{DateTime = dt2, Name = "n2",},
new AvailableSlot{DateTime = dt1, Name = "n3",},
};
var list2 = new List<AvailableSlot>
{
new AvailableSlot{DateTime = dt1, Name = "n1",},
new AvailableSlot{DateTime = dt2, Name = "n2",},
new AvailableSlot{DateTime = dt3, Name = "n3",},
};
var intersected = list1.Select (l => l.DateTime).
Intersect(list2.Select (l => l.DateTime));
var lookup = list1.Union(list2).ToLookup (
slot => slot.DateTime, slot => slot);
lookup.Where (l => intersected.Contains(l.Key)).Select (
slot => new
{
DateTime=slot.Key,
Names=slot.Select (s => s.Name)
});
Which in this case gives the result:
DateTime Names
01/02/2013 00:00 n1
n3
n1
01/03/2013 00:00 n2
n2
You could of course use Names=slot.Select(s => s.Name).Distinct() to get a distinct list of names.
Related
what I am trying to achieve here is that I want from that LINQ query to return the list with two poperties: billNo, and number of occurences of the importcode on the same fromDate.
So here we have a billNo 1 and 2 both have the same importcode which appears in two rows on the same date (01/01/2020) thus count is 2.
If it helps to clarify, think of it as import code should only cover one distinct fromDate. If it appears multiple time, I would like to see how many times (count) and which BillNo s are like that.
So expected result for dataset below would be:
BillNo
Count
1
2
2
2
3
1
4
1
5
1
I struggle to figure out how to select BillNo if it is not used for grouping.
Thanks a lot for helping.
var rows = new List<ImportRow>()
{
new ImportRow {billNo= 1, importCode = "one", fromDate = new DateTime(2020, 1, 1)},
new ImportRow {billNo= 2, importCode = "one", fromDate = new DateTime(2020, 1, 1)},
new ImportRow {billNo= 3, importCode = "two", fromDate = new DateTime(2020, 1, 1)},
new ImportRow {billNo= 4, importCode = "two", fromDate = new DateTime(2020, 2, 1)},
new ImportRow {billNo= 5, importCode = "one", fromDate = new DateTime(2020, 3, 1)}
};
public class ImportRow : IEnumerable
{
public int billNo { get; set; }
public string importCode { get; set; }
public DateTime fromDate { get; set; }
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
var billNosWithCounts = rows.GroupBy(info => new { info.importCode,
info.fromDate })
.Select(group => new
{
FromDate = group.Key.fromDate,
Count = group.Count()
});
You can join the source onto itself, I believe that will give you the output you are looking for:
var result = from r1 in rows
join r2 in rows
on new { r1.importCode, r1.fromDate } equals
new { r2.importCode, r2.fromDate }
group r1.billNo by r1.billNo into g
select new
{
BillNo = g.Key,
Count = g.Count()
};
You could potentially achieve what you are looking for by doing the following:
public static IDictionary<int, string> Map(this List<Sample> collection)
{
var kvp = new Dictionary<int, string>();
var records = collection.GroupBy(value => new { value.Code, value.Date });
foreach(var record in records)
foreach(var contents in record)
kvp.Add(contents.Bill, contents.Count());
}
A sample of the object you outlined above:
var collection = new List<Sample>()
{
new Sample() { Bill = 1, Code = "ABC", Date = DateTime.Now.ToShortDateString() },
new Sample() { Bill = 2, Code = "ABC", Date = DateTime.Now.ToShortDateString() },
new Sample() { Bill = 3, Code = "ABCD", Date = DateTime.Now.ToShortDateString() },
new Sample() { Bill = 4, Code = "ABCDE", Date = DateTime.Now.AddDays(-3).ToShortDateString() }
};
In essence, I created a method that will let you pass an object. It will then group on the import code and the date. Creating the unique pair you desire. Then I simply add the newly transformed content into a Dictionary. You could use Linq to do the full transform, but it might become more difficult to read or understand, so I chose to simplify into basic loops for the transformation.
myClass structure :
public class myClass
{
public string Name { get; set; }
public string AdditionalData { get; set; }
public System.DateTime ActivityTime { get; set; }
}
I have a list of the above class List<myClass> all ordered by ActivityTime.
I need to split the above list and get a List<List<myClass>> such that if there is a difference of more than a specific period say 5 mins between two consecutive ActivityTime I wish the split to take place.
Any help is sincerely appreciated.
Thanks
You can do that in a simple iteration:
var myList = new List<myClass>()
{
new myClass() { Name = "ABC", AdditionalData = "1", ActivityTime = DateTime.Now },
new myClass() { Name = "ABC2", AdditionalData = "2", ActivityTime = DateTime.Now },
new myClass() { Name = "ABC3", AdditionalData = "3", ActivityTime = DateTime.Now.AddSeconds(6) },
new myClass() { Name = "ABC4", AdditionalData = "3", ActivityTime = DateTime.Now.AddSeconds(11) },
new myClass() { Name = "ABC4", AdditionalData = "3", ActivityTime = DateTime.Now.AddSeconds(12) }
};
var results = new List<List<myClass>>();
myClass previousItem = null;
List<myClass> currentList = new List<myClass>();
foreach (var item in myList)
{
if (previousItem == null || (item.ActivityTime - previousItem.ActivityTime).TotalSeconds >= 5)
{
currentList = new List<myClass>();
results.Add(currentList);
}
currentList.Add(item);
previousItem = item;
}
What about this solution:
var data = new List<myClass> {
new myClass { ActivityTime = new DateTime(2016, 01, 01, 01, 00, 00) },
new myClass { ActivityTime = new DateTime(2016, 01, 01, 01, 05, 00) },
new myClass { ActivityTime = new DateTime(2016, 01, 01, 01, 06, 00) },
new myClass { ActivityTime = new DateTime(2016, 01, 01, 01, 07, 00) },
new myClass { ActivityTime = new DateTime(2016, 01, 01, 01, 17, 00) }
};
var period = 5;
var firstActivityTime = data.Min(x => x.ActivityTime);
var answer = data.OrderBy(x => x.ActivityTime).GroupBy(x => {
var dif = (x.ActivityTime - firstActivityTime).Minutes;
return dif / period - (dif % period == 0 && dif / period != 0 ? 1 : 0);
}).Select(x => x.ToList()).ToList();
Various approaches this can be achieved, but underlying principle is same. Keep track of n-1(th) element when processing n(th) element and calculate timespan between these two.
You could do something like this.
List<MyClass> data = ...; // input
int gid=0;
DateTime prevvalue = data[0].ActivityTime; // Initial value
var result = data.Select(x=>
{
var obj = x.ActivityTime.Subtract(prevvalue).TotalMinutes<5? // Look for timespan difference
new {gid= gid, item =x} // Create groups based on consecutive gaps.
:new {gid= ++gid, item =x};
prevvalue= x.ActivityTime; // Keep track of previous value (for next element process)
return obj;
})
.GroupBy(x=>x.gid) // Split into groups
.Select(x=>x.Select(s=>s.item).ToList())
.ToList();
Check this Demo
I have a List with dates to show it in a grid and I would like add one hour to range2:
private void grid_loaded(object sender, RoutedEventArgs e)
{
var data = new List<EntityViewModel>
{
new EntityViewModel { aDay = new DateTime(1980, 1, 1, 12, 0, 12) },
new EntityViewModel { aDay = new DateTime(1983, 1, 1, 12, 23, 12) },
new EntityViewModel { aDay = new DateTime(1985, 6, 14, 12, 0, 12) },
new EntityViewModel { aDay = new DateTime(1990, 7, 3, 12, 23, 12) },
new EntityViewModel { aDay = new DateTime(1995, 8, 1, 4, 23, 12) },
new EntityViewModel { aDay = new DateTime(1996, 1, 1, 12, 0, 12) },
new EntityViewModel { aDay = new DateTime(2000, 1, 1, 12, 0, 12) },
new EntityViewModel { aDay = DateTime.Now }
};
var range = data.Where(i => i.aDay.Year >= 1983 && i.aDay.Year <= 1996).ToList();
var range2 = range.Where(i => i.aDay.Date.IsDaylightSavingTime() == true).ToList();
}
I tried this:
var range3 = range.Where(i => i.aDay.Date.IsDaylightSavingTime() == true).ToList();
range3.ForEach(i => i.aDay.AddHours(1));
this:
foreach (var item in range2.Where(x => x.aDay != null))
{
item.aDay.AddHours(1);
}
and this:
var range5 = range2.Where(i => i.aDay != null).Select(i => { i.aDay.AddHours(1); return i; }).ToList();
But it doesn't do anything, are always the same hour.
AddHours does NOT alter the initial date, but return a new one.
You need to select these dates:
var range5 = range2.Where(i => i.aDay != null).Select(i => i.aDay.AddHours(1));
To get the altered dates or
range3.ForEach(i => i.aDay = i.aDay.AddHours(1));
To alter the dates of the items in the list.
DateTime is a structure, so you have to do:
var date = DateTime.Now;
date = date.AddHours(1);
in your case:
range3.ForEach(i => i.aDay = i.aDay.AddHours(1));
AddHours returns a new DateTime, it does not change the current instance.
http://msdn.microsoft.com/en-us/library/system.datetime.addhours.aspx
for (int i = 0; i < range2.Count; ++i) {
range2[i].aDay = range2[i].aDay.AddHours(1);
}
I have an object and two lists as follows:
public class MyObject
{
public int Key;
public DateTime Day;
public decimal Value;
}
List<MyObject> listA = new List<MyObject>()
{
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = 8 },
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 18), Value = 8 },
new MyObject() { Key = 2, Day = new DateTime(2012, 12, 17), Value = 8 },
new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 4 },
new MyObject() { Key = 4, Day = new DateTime(2012, 12, 17), Value = 4 }
};
List<MyObject> listB = new List<MyObject>()
{
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = 2 },
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 18), Value = 8 },
new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 8 },
new MyObject() { Key = 4, Day = new DateTime(2012, 12, 17), Value = 4 },
new MyObject() { Key = 5, Day = new DateTime(2012, 12, 17), Value = 10 }
};
The results I am looking for are:
List<MyObject> listChanges = new List<MyObject>()
{
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = -6 },
new MyObject() { Key = 2, Day = new DateTime(2012, 12, 17), Value = -8 },
new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 4 },
new MyObject() { Key = 5, Day = new DateTime(2012, 12, 17), Value = 10 }
};
Essentially, I am trying to create a list which contains the changes that would be required to make listA into listB. Thus, while Except, and Intersect from LINQ could be used I don't think they will have the best performance to do such a task as you would still need another comparison to get the difference in the values.
One thought I had is: If I loop through listA, I can remove the item from listA and from listB (if it is found and at this time I can determine the +/- differences). Once I have finished with listA, listB would only contain additions.
How can I get the change results?
This should do it. It'll throw an exception if any of your Key/Day combinations are not unique within either of your inputs.
public static IEnumerable<MyObject> GetChanges(
IEnumerable<MyObject> from, IEnumerable<MyObject> to)
{
var dict = to.ToDictionary(mo => new { mo.Key, mo.Day });
// Check that keys are distinct in from, too:
var throwaway = from.ToDictionary(mo => new { mo.Key, mo.Day });
// Adjustments of items found in "from"
foreach (MyObject mo in from)
{
var key = new { mo.Key, mo.Day };
MyObject newVal;
if (dict.TryGetValue(key, out newVal))
{
// Return item indicating adjustment
yield return new MyObject {
Key = mo.Key, Day = mo.Day, Value = newVal.Value - mo.Value };
dict.Remove(key);
}
else
{
// Return item indicating removal
yield return new MyObject {
Key = mo.Key, Day = mo.Day, Value = -mo.Value };
}
}
// Creation of new items found in "to"
foreach (MyObject mo in dict.Values)
{
// Return item indicating addition
// (Clone as all our other yields are new objects)
yield return new MyObject {
Key = mo.Key, Day = mo.Day, Value = mo.Value };
}
}
You could speed this up by removing the uniqueness check on from or doing it on-the-fly (try adding each item's key-parts to a HashSet), but I don't think you can avoid looping over part of to twice - once to build the dictionary, once to return the remainder.
First I would implement an IEqualityComparer<T> that checks equality based on both the Key and Day properties. Then you could use linq as follows:
var notInA = listB.Except(listA, myEqualityComparer);
var notInB = listA.Except(listB, myEqualityComparer)
.Select(o => {
return new MyObject {
Key = item.Key,
Day = item.Day,
Value = item.Value * -1
};
});
var listA2 = listA.Intersect(listB, myEqualityComparer)
.OrderBy(o => o.Key)
.ThenBy(o => o.Day);
var listB2 = listB.Intersect(listA, myEqualityComparer)
.OrderBy(o => o.Key)
.ThenBy(o => o.Day);
var diff = listA2.Zip(listB2, (first,second) => {
return new MyObject {
Key = first.Key,
Day = first.Day,
Value = second.Value - first.Value
});
diff = diff.Concat(notInA).Concat(notInB);
how about this :
List<MyObject> listA = new List<MyObject>(){
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = 8 },
new MyObject() { Key = 2, Day = new DateTime(2012, 12, 17), Value = 8 },
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 18), Value = 8 },
new MyObject() { Key = 4, Day = new DateTime(2012, 12, 17), Value = 4 },
new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 4 }
};
List<MyObject> listB = new List<MyObject>(){
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 17), Value = 2 },
new MyObject() { Key = 3, Day = new DateTime(2012, 12, 17), Value = 8 },
new MyObject() { Key = 4, Day = new DateTime(2012, 12, 17), Value = 4 },
new MyObject() { Key = 1, Day = new DateTime(2012, 12, 18), Value = 8 },
new MyObject() { Key = 5, Day = new DateTime(2012, 12, 17), Value = 10 }
};
List<MyObject> listChanges = Comparer(listA, listB);
MyObject[] hasil = listChanges.ToArray();
for (int a = 0; a < hasil.Length;a++ ) {
Console.WriteLine(hasil[a].Key+" "+hasil[a].Day+" "+hasil[a].Value);
}
and the function :
private MyObject[] sort(List<MyObject> input) {
//sort input with it's key
MyObject[] gg = input.ToArray();
for (int a = 0; a < input.Count; a++) {
for (int b = a + 1; b < input.Count; b++) {
if (gg[a].Key > gg[b].Key) {
MyObject temp = gg[a];
gg[a] = gg[b];
gg[b] = temp;
}
}
}
//sort input, if key is same => sort the date
for (int a = 0; a < input.Count; a++) {
int indStart = a;
int indEnd = a;
for (int b = a + 1; b < input.Count; b++) {
if (gg[a].Key == gg[b].Key) {
indEnd++;
} else {
b = input.Count;
}
}
a = indEnd;
for (int c = indStart; c <= indEnd; c++) {
for (int d = c + 1; d <= indEnd; d++) {
if (gg[c].Day > gg[d].Day) {
MyObject temp = gg[c];
gg[c] = gg[d];
gg[d] = temp;
}
}
}
}
return gg;
}
private List<MyObject> Comparer(List<MyObject> listA, List<MyObject> listB) {
List<MyObject> output = new List<MyObject>();
//if you sure that the list was sorted, u just remove the sort function
MyObject[] ff = sort(listA);
MyObject[] gg = sort(listB);
Boolean[] masuk = new Boolean[gg.Length];
//foreach element in listA, search the changes in input
for (int a = 0; a < listA.Count;a++ ) {
//find element in input which contains the changes of element in listA
Boolean ins = false;
for (int b = 0; b < listB.Count;b++ ) {
if (masuk[b])
continue;
if (ff[a].Key >= gg[b].Key) {
if (ff[a].Key == gg[b].Key && ff[a].Day == gg[b].Day){
masuk[b] = true;
if (gg[b].Value != ff[a].Value) {
output.Add(new MyObject() { Key = gg[b].Key, Day = gg[b].Day, Value = gg[b].Value - ff[a].Value });
b = listB.Count;
}
ins = true;
}
} else {
b = listB.Count;
}
}
if (!ins) {
output.Add(new MyObject() { Key = ff[a].Key, Day = ff[a].Day, Value = -ff[a].Value });
}
}
for (int a = 0; a < gg.Length;a++ ) {
if(!masuk[a]){
output.Add(new MyObject() { Key = gg[a].Key, Day = gg[a].Day, Value = gg[a].Value });
}
}
return output;
}
and the output :
1 12/17/2012 12:00:00 AM -6
2 12/17/2012 12:00:00 AM -8
3 12/17/2012 12:00:00 AM 4
5 12/17/2012 12:00:00 AM 10
Have a list like this:
01/01/2009, 120
04/01/2009, 121
30/12/2009, 520
01/01/2010, 100
04/01/2010, 101
31/12/2010, 540
I need to find the last value for each year, e.g. the result would be 520, 540?
var lastValues = records.OrderByDescending(r => r.Date)
.GroupBy(r => r.Date.Year)
.Select(g => g.First().Value);
class Program
{
static void Main()
{
var list = new[]
{
new { Date = new DateTime(2009, 1, 1), Value = 120 },
new { Date = new DateTime(2009, 4, 1), Value = 121 },
new { Date = new DateTime(2009, 12, 30), Value = 520 },
new { Date = new DateTime(2010, 1, 1), Value = 100 },
new { Date = new DateTime(2009, 4, 1), Value = 101 },
new { Date = new DateTime(2010, 12, 31), Value = 540 },
};
var result = list
.GroupBy(x => x.Date.Year)
.Select(g => new { Date = g.Key, MaxValue = g.Max(x => x.Value) });
foreach (var item in result)
{
Console.WriteLine(item);
}
}
}