Select Max value from sublist with LINQ - c#

How to write a LINQ query that would return row of type "Value" with Max Date and Max value for that date. it should be the row where name = "Correct"
I've written a query at the end, and it working, just trying to find a correct way of doing this.
Thanks in advance
public class MeasurePoint
{
public int No { get; set; }
public string Weight { get; set; }
public List<Values> Vals { get; set; }
}
public class Values
{
public string Name { get; set; }
public int Val { get; set; }
public DateTime Date { get; set; }
}
public static class Test
{
public static void Calc()
{
var mps = new List<MeasurePoint>();
mps.Add(new MeasurePoint()
{
No = 1,
Vals = new List<Values>()
{
new Values(){Date = DateTime.Now.Date.AddDays(1), Name = "testas", Val = 1},
new Values(){Date = DateTime.Now.Date.AddDays(2), Name = "testas", Val = 5},
new Values(){Date = DateTime.Now.Date, Name = "testas", Val = 15}
}});
mps.Add(new MeasurePoint()
{
No = 2,
Vals = new List<Values>()
{
new Values(){Date = DateTime.Now.Date, Name = "testas", Val = 11},
new Values(){Date = DateTime.Now.Date.AddDays(2), Name = "Correct", Val = 55},
new Values(){Date = DateTime.Now.Date, Name = "testas", Val = 15}
}
});
mps.Add(new MeasurePoint()
{
No = 3,
Vals = new List<Values>()
{
new Values(){Date = DateTime.Now.Date.AddDays(1), Name = "testas", Val = 111},
new Values(){Date = DateTime.Now.Date.AddDays(2), Name = "testas", Val = 52},
new Values(){Date = DateTime.Now.Date, Name = "testas", Val = 15}
}
});
mps.Add(new MeasurePoint()
{
No = 4,
Vals = new List<Values>()
});
var x = mps.ElementAt(0).Vals.Union(mps.ElementAt(1).Vals).Union(mps.ElementAt(2).Vals);
var z = x.Where(p => p.Date == x.Max(d => d.Date)).MaxBy(t=>t.Val);
//One more way I've found
var ttt = mps.SelectMany(p => p.Vals).GroupBy(t=>t.Date).MaxBy(r=>r.Key).MaxBy(g=>g.Val);
}

var max = mps.SelectMany(x => x.Vals)
.Aggregate((a, x) => (x.Date > a.Date) ||
((x.Date == a.Date) && (x.Val > a.Val)) ? x : a);

try this
var result = mps.Where(m => m.Vals.Count > 0)
.SelectMany(m => m.Vals
.OrderByDescending(v => v.Date)
.Take(1), (m, v) => new {m.No, v.Date, v.Name, v.Val});
EDIT - this is a new version as issue has become more clear
var result = mps.Where(m => m.Vals.Count > 0)
.SelectMany(m => m.Vals)
.OrderByDescending(v => v.Date)
.ThenByDescending(v => v.Val).Take(1);

How can use call MaxBy() if it's a method of IObservable<T> meanwhile GroupBy() returns IEnumerable<T> ?

Related

C#: how to compare "Size" along with new item

I have 2 List classes below,
public class OldList
{
public string Name { get; set; }
public int Size { get; set; }
}
public class NewList
{
public string Name { get; set; }
public int Size { get; set; }
}
Now I have data from 2 class likes,
var oldList = new List<OldList> {
new OldList { Name = "F1", Size = 374 },
new OldList { Name = "F2", Size = 125 }
};
var newList = new List<NewList> {
new NewList { Name = "F1", Size = 374, },
new NewList { Name = "F2", Size = 126, },
new NewList { Name = "F3", Size = 13, }
};
I would like to filter newList to retrieve all new items (e.g. "F3") and also all existing items where Size of newList is greater than Size of OldList (e.g. "F2").
For "F1" the Size is the same, hence I want to ignore.
Below code gave me result "F3", how to also get "F2"?
var X = newList.Where(p => !oldList.Any(l => p.Name == l.Name));
You can add an additional condition for "same name, increased size" into the .Where statement, like this:
var X = newList.Where(
p => !oldList.Any(l => p.Name == l.Name)
|| oldList.Any(l => p.Name == l.Name && p.Size > l.Size)
);
Try this?
var result = newList.Where(i => !oldList.Any(l => i.Name == l.Name)
|| i.Size > oldList.Where(x => x.Name == i.Name).Select(x => x.Size).Max());
Note: performance of this won't be great and this may not translate to SQL if you're using ORM.

c# set entity properties from another key value entity

I have an entity which contains key and values. My key is enum. Values are string on this entity (but their data type may differ from string).
Enum example:
public enum CallKey {
CallDate = 1,
CallTime = 2,
FromPhoneNumber = 3,
ToPhoneNumber = 4,
Duration = 5,
FromOperatorCode = 6,
ToOperatorCode = 7
}
My key value entity is:
public class CallKeyValue {
public CallKey CallKey { get; set; }
public string Value1 { get; set; }
public string Value2 { get; set; }
}
My example key, value data is:
CallKey |Value1 |Value2
1 |11.04.2017 |
2 |15:43 |
3 |5311234567 |
4 |5311234587 |
5 |13*min |
6 |TR |001
7 |TR |002
Now, I want to create my final entity from my key value entity.
My final entity:
public class CallDetail{
public DateTime CallDate { get; set; } //=15.04.2017 15:43
public string FromPhoneNumber { get; set; } //=5311234567
public string ToPhoneNumber { get; set; } //=5311234587
public int Duration { get; set; } //=13
public DurationUnit DurationUnit { get; set; } //=1 (this is enum 1:min, 2:hour etc...)
public string FromOperatorCountry { get; set; } //=TR
public string FromOperatorId { get; set; } //=001
public string ToOperatorCountry { get; set; } //=TR
public string ToOperatorId { get; set; } //=002
}
Which way to set CallDetail entity? Reflection, property by property or another better way?
Ok, I think I found some solutions about this problem. I did 3 tests and I add here a simple sample. I think second is best for me, but I decide to do a loading test.
public class CallTest {
public void TestCode() {
var items = new List<CallKeyValue>
{
new CallKeyValue {CallKey = CallKey.CallDate, Value1 = "11.04.2017"},
new CallKeyValue {CallKey = CallKey.CallTime, Value1 = "15:43"},
new CallKeyValue {CallKey = CallKey.FromPhoneNumber, Value1 = "5311234567"},
new CallKeyValue {CallKey = CallKey.ToPhoneNumber, Value1 = "5311234587 "},
new CallKeyValue {CallKey = CallKey.Duration, Value1 = "13*min"},
new CallKeyValue {CallKey = CallKey.FromOperatorCode, Value1 = "TR",Value2 = "001"},
new CallKeyValue {CallKey = CallKey.ToOperatorCode, Value1 = "TR",Value2 = "002"},
};
var st = new Stopwatch();
//Test 1: ElapsedMilliseconds: 50, ElapsedTicks: 122023
st.Start();
IDictionary<string,dynamic> expando = new ExpandoObject();
foreach(var item in items) {
expando.Add(item.CallKey.ToString(),null);
expando[item.CallKey.ToString()] = new { Value1 = item.Value1,Value2 = item.Value2 };
}
var cd = new CallDetail {
CallDate = DateTime.Parse(expando[CallKey.CallDate.ToString()].Value1 + " " + expando[CallKey.CallTime.ToString()].Value1),
FromPhoneNumber = expando[CallKey.FromPhoneNumber.ToString()].Value1,
ToPhoneNumber = expando[CallKey.ToPhoneNumber.ToString()].Value1,
Duration = Convert.ToInt32(expando[CallKey.Duration.ToString()].Value1.ToString().Split('*')[0]),
DurationUnit = GetEnumKeyOfUnit(expando[CallKey.Duration.ToString()].Value1.ToString().Split('*')[1]),
FromOperatorCountry = expando[CallKey.FromOperatorCode.ToString()].Value1,
FromOperatorId = expando[CallKey.FromOperatorCode.ToString()].Value2,
ToOperatorCountry = expando[CallKey.ToOperatorCode.ToString()].Value1,
ToOperatorId = expando[CallKey.ToOperatorCode.ToString()].Value2
};
st.Stop();
Console.WriteLine(st.ElapsedMilliseconds);
Console.WriteLine(st.ElapsedTicks);
st.Reset();
//Test 2: ElapsedMilliseconds: 0, ElapsedTicks: 328
st.Start();
var cd3 = items.Select(s => {
IDictionary<string,dynamic> expando2 = new ExpandoObject();
expando2.Add(s.CallKey.ToString(),null);
expando2[s.CallKey.ToString()] = new { Value1 = s.Value1,Value2 = s.Value2 };
var item = new CallDetail {
CallDate = DateTime.Parse(expando2[CallKey.CallDate.ToString()].Value1 + " " + expando2[CallKey.CallTime.ToString()].Value1),
FromPhoneNumber = expando2[CallKey.FromPhoneNumber.ToString()].Value1,
ToPhoneNumber = expando2[CallKey.ToPhoneNumber.ToString()].Value1,
Duration = Convert.ToInt32(expando2[CallKey.Duration.ToString()].Value1.ToString().Split('*')[0]),
DurationUnit = GetEnumKeyOfUnit(expando2[CallKey.Duration.ToString()].Value1.ToString().Split('*')[1]),
FromOperatorCountry = expando2[CallKey.FromOperatorCode.ToString()].Value1,
FromOperatorId = expando2[CallKey.FromOperatorCode.ToString()].Value2,
ToOperatorCountry = expando2[CallKey.ToOperatorCode.ToString()].Value1,
ToOperatorId = expando2[CallKey.ToOperatorCode.ToString()].Value2
};
return item;
});
st.Stop();
Console.WriteLine(st.ElapsedMilliseconds);
Console.WriteLine(st.ElapsedTicks);
st.Reset();
//Test 3: ElapsedMilliseconds: 0, ElapsedTicks: 1390
st.Start();
var cd2 = new CallDetail {
CallDate = DateTime.Parse(items.FirstOrDefault(f => f.CallKey == CallKey.CallDate)?.Value1 + " " + items.FirstOrDefault(f => f.CallKey == CallKey.CallTime)?.Value1),
FromPhoneNumber = items.FirstOrDefault(f => f.CallKey == CallKey.FromPhoneNumber)?.Value1,
ToPhoneNumber = items.FirstOrDefault(f => f.CallKey == CallKey.ToPhoneNumber)?.Value1,
Duration = Convert.ToInt32(items.FirstOrDefault(f => f.CallKey == CallKey.Duration)?.Value1.Split('*')[0]),
DurationUnit = GetEnumKeyOfUnit(items.FirstOrDefault(f => f.CallKey == CallKey.Duration)?.Value1.Split('*')[1]),
FromOperatorCountry = items.FirstOrDefault(f => f.CallKey == CallKey.FromOperatorCode)?.Value1,
FromOperatorId = items.FirstOrDefault(f => f.CallKey == CallKey.FromOperatorCode)?.Value2,
ToOperatorCountry = items.FirstOrDefault(f => f.CallKey == CallKey.ToOperatorCode)?.Value1,
ToOperatorId = items.FirstOrDefault(f => f.CallKey == CallKey.ToOperatorCode)?.Value2
};
st.Stop();
Console.WriteLine(st.ElapsedMilliseconds);
Console.WriteLine(st.ElapsedTicks);
}
private DurationUnit? GetEnumKeyOfUnit(string unit) {
switch(unit) {
case "min":
return DurationUnit.Minute;
case "hour":
return DurationUnit.Hour;
}
return null;
}
}
Before this test, I try to use reflection. Reflection performance is very bad than 3 tests. It is elapsedmilliseconds is about 100, also elapsedticks is very high. I may use second test until find to better way.

C# - Filling List with LINQ from another List<>

Here's my main class:
public class Subject
{
public struct Class
{
public byte Day { get; set; }
public DateTime Time { get; set; }
}
public string Name { get; set; }
public List<Class> Data { get; set; }
}
For example,
List<Subject> subjects = new List<Subject>
{
new Subject()
{
Name = "Math",
Data = new List<Class>()
{
new Class { Day = 2, Time = Convert.ToDateTime("8:30") },
new Class { Day = 2, Time = Convert.ToDateTime("10:25") }
}
},
new Subject()
{
Name = "Astronomy",
Data = new List<Class>()
{
new Class { Day = 2, Time = Convert.ToDateTime("12:30") },
new Class { Day = 4, Time = Convert.ToDateTime("14:30") }
}
},
new Subject()
{
Name = "Chemistry",
Data = new List<Class>()
{
new Class { Day = 3, Time = Convert.ToDateTime("8:30") }
}
},
new Subject()
{
Name = "Physics",
Data = new List<Class>()
{
new Class { Day = 3, Time = Convert.ToDateTime("10:25") },
new Class { Day = 4, Time = Convert.ToDateTime("12:30") }
}
}
};
The data above is filling up by parsing JSON.
But now I need to do next:
1.Select all distinct Day (in this case: 2, 3, 4);
2. Fill this days with subject (Name and Time).
I created this:
public class Schedule
{
public struct Subject
{
public string Name { get; set; }
public DateTime Time { get; set; }
}
public struct Day
{
public byte DayOfWeek { get; set; }
public List<Subject> Subjects { get; set; }
}
public List<Day> Days { get; set; }
}
so I except to do something like this:
Schedule schedule = new Schedule();
schedule.Days = new List<Schedule.Day>()
{
new Schedule.Day()
{
DayOfWeek = 2,
Subjects = new List<Schedule.Subject>()
{
new Schedule.Subject() { Name = "Math", Time = Convert.ToDateTime("8:30") },
new Schedule.Subject() { Name = "Math", Time = Convert.ToDateTime("10:25") },
new Schedule.Subject() { Name = "Astronomy", Time = Convert.ToDateTime("12:30") }
}
},
new Schedule.Day()
{
DayOfWeek = 3,
Subjects = new List<Schedule.Subject>()
{
new Schedule.Subject() { Name = "Chemistry", Time = Convert.ToDateTime("8:30") },
new Schedule.Subject() { Name = "Physics", Time = Convert.ToDateTime("10:25") },
}
},
new Schedule.Day()
{
DayOfWeek = 4,
Subjects = new List<Schedule.Subject>()
{
new Schedule.Subject() { Name = "Physics", Time = Convert.ToDateTime("12:30") },
new Schedule.Subject() { Name = "Astronomy", Time = Convert.ToDateTime("14:30") }
}
}
};
The question is How I can select data to Schedule from List<Subjects> with LINQ (I don't wanna use loops).
var result = subjects
.SelectMany(s => s.Data.Select(x => new { s.Name, x.Day, x.Time }))
.GroupBy(x => x.Day)
.Select(g => new Schedule.Day
{
DayOfWeek = g.Key,
Subjects = g.Select(item => new Schedule.Subject
{
Name = item.Name,
Time = item.Time
})
.OrderBy(item => item.Time)
.ToList()
})
.OrderBy(gItem => gItem.DayOfWeek)
.ToList();
And then....
Schedule schedule = new Schedule();
schedule.Days = result;
It's also weird to put the List<Day> inside the schedule class.
Out of fun I've rewritten the solution to promote the Query-Expression Syntax.
IEnumerable<Schedule.Day> scheduledDays =
from subj in subjList
from cl in subj.Data
select new { Class = cl, SubjectName = subj.Name } into classWithSubject
group classWithSubject by classWithSubject.Class.Day into classesByDay
orderby classesByDay.Key
select new Schedule.Day()
{
DayOfWeek = classesByDay.Key,
Subjects = (from cl in classesByDay
orderby cl.Class.Time
select new Schedule.Subject() { Name = cl.SubjectName, Time = cl.Class.Time }).ToList()
};
Schedule sched = new Schedule() { Days = scheduledDays.ToList() };
If I right understood, You can use ForEach LINQ:
schedule.Days.ForEach(x => x.Subjects.ForEach(y => Console.WriteLine($"{y.Name}, {y.Time}")));
Instead of
Console.WriteLine($"{y.Name}, {y.Time}")
You can write this info into some variables.
P.S. ForEach will pass all elements of list.
Try this:
var newQuery = subjects.SelectMany(y => y.Data.Select(x=> new {x.Day,x.Time,y.Name }));
var newQuery2 = newQuery.OrderBy(x=>x.Day).OrderBy(x=>x.Time).GroupBy(x => x.Day);
var newQuery3 = newQuery2.Select(x => new Schedule.Day
{
DayOfWeek = x.Key,
Subjects = x.Select(y => new Schedule.Subject
{
Time = y.Time,
Name = y.Name
}).ToList()
});

String Join Using a Lambda Expression

If I have a list of some class like this:
class Info {
public string Name { get; set; }
public int Count { get; set; }
}
List<Info> newInfo = new List<Info>()
{
{new Info { Name = "ONE", Count = 1 }},
{new Info { Name = "TWO", Count = 2 }},
{new Info { Name = "SIX", Count = 6 }}
};
Can a Lambda expression be used to string join the attributes in the list of classes like this:
"ONE(1), TWO(2), SIX(6)"
string.Join(", ", newInfo.Select(i => string.Format("{0}({1})", i.Name, i.Count)))
You could also override ToString.
class Info
{
....
public override ToString()
{
return string.Format("{0}({1})", Name, Count);
}
}
... and then the call is dead simple (.Net 4.0):
string.Join(", ", newInfo);
String.Join(", ", newInfo.Select(i=>i.Name+"("+i.Count+")") );
You Can use as like following
You can Return a specific type like this
Patient pt = dc.Patients.Join(dc.PatientDetails, pm => pm.PatientId, pd => pd.PatientId,
(pm, pd) => new
{
pmm = pm,
pdd = pd
})
.Where(i => i.pmm.PatientCode == patientCode && i.pmm.IsActive || i.pdd.Mobile.Contains(patientCode))
.Select(s => new Patient
{
PatientId = s.pmm.PatientId,
PatientCode = s.pmm.PatientCode,
DateOfBirth = s.pmm.DateOfBirth,
IsActive = s.pmm.IsActive,
UpdatedOn = s.pmm.UpdatedOn,
UpdatedBy = s.pmm.UpdatedBy,
CreatedOn = s.pmm.CreatedOn,
CreatedBy = s.pmm.CreatedBy
})
Or You can retrieve anonymous type like this
var patientDetails = dc.Patients.Join(dc.PatientDetails, pm => pm.PatientId, pd => pd.PatientId,
(pm, pd) => new
{
pmm = pm,
pdd = pd
})
.Where(i => i.pmm.PatientCode == patientCode && i.pmm.IsActive || i.pdd.Mobile.Contains(patientCode))
.Select(s => new
{
PatientId = s.pmm.PatientId,
PatientCode = s.pmm.PatientCode,
DateOfBirth = s.pmm.DateOfBirth,
IsActive = s.pmm.IsActive,
PatientMobile = s.pdd.Mobile,
s.pdd.Email,
s.pdd.District,
s.pdd.Age,
s.pdd.SittingId
})

LINQ Lambda Group By with Sum

Hi I can do this in method syntax but I'm trying to improve my lambda skills how can I do:
SELECT SUM([job_group_quota]) as 'SUM'
FROM [dbo].[tbl_job_session]
WHERE [job_group_job_number] = #jobnum
and [job_group_ID] like #sess
GROUP BY [job_group_job_number]
I've been messing around with it but can't get it right.
lnq.tbl_job_sessions.GroupBy(a => a.job_group_job_number == jnum)
.Select(b => new { b.job_group_quota}).Sum();
A general example:
query
.GroupBy(item => item.GroupKey)
.Select(group => group.Sum(item => item.Aggregate));
Few Group by Examples
public void GroupBy1()
{
var personList = dbEntities.People.GroupBy(m => m.PersonType).Select(m => new { PersonType = m.Key, Count = m.Count() });
}
public void GroupBy2()
{
var personList = dbEntities.People.GroupBy(m => new { m.PersonType, m.FirstName }).Select(m => new { PersonType = m.Key, Count = m.Count() });
}
public void GroupBy3()
{
var personList = dbEntities.People.Where(m => m.EmailPromotion != 0).GroupBy(m => new { m.PersonType, m.FirstName }).Select(m => new { PersonType = m.Key, Count = m.Count() });
}
public void GroupBy4()
{
var personList = dbEntities.People.GroupBy(m => new { m.PersonType, m.FirstName }).Where(m => m.Count() > 70).Select(m => new { PersonType = m.Key, Count = m.Count() });
}
public void GroupBy5()
{
var personList = dbEntities.People
.GroupBy(m =>
new
{
m.PersonType
}).Where(m => m.Count() > 70)
.Select(m =>
new
{
PersonType = m.Key,
Count = m.Count()
});
var list1 = dbEntities.People.
GroupBy(m => new { m.PersonType }).
Select(m =>
new
{
Type = m.Key,
Count = m.Count()
})
.Where(
m => m.Count > 70
&& m.Type.PersonType.Equals("EM")
|| m.Type.PersonType.Equals("GC"));
}
public void GroupBy6()
{
var list1 = dbEntities.People.
GroupBy(m => new { m.PersonType, m.EmailPromotion }).Select(m =>
new
{
Type = m.Key,
Count = m.Count()
})
.Where
(
m => m.Count > 70 && m.Type.EmailPromotion.Equals(0) &&
(
m.Type.PersonType.Equals("EM") ||
m.Type.PersonType.Equals("GC")
));
}
public void GroupBy7()
{
var list1 = dbEntities.People.
GroupBy(m => m.PersonType).
Select(c =>
new
{
Type = c.Key,
Total = c.Sum(p => p.BusinessEntityID)
});
}
public void GroupBy8()
{
var list1 = dbEntities.People.
GroupBy(m => m.PersonType).
Select(c =>
new
{
Type = c.Key,
Count = c.Count(),
Total = c.Sum(p => p.BusinessEntityID)
});
}
public void GroupBy9()
{
var list1 = dbEntities.People.
GroupBy(m => m.PersonType).
Select(c =>
new
{
Type = c.Key,
Max = c.Max(),
});
}
If you want to get a key-to-sum Dictionary result.
var allJobQuota = jobSessions.GroupBy(s => s.jobNumber)
.ToDictionary(g => g.Key, g => g.Sum(s => s.quota));
This example shows how to iterate the grouped values getting the key and totals, and how to get totals directly (like previous). Both using only lambda operator.
using System;
using System.Collections.Generic;
using System.Linq;
public class Person
{
public string Name { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
public int SomeValue { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Person> data = GetPopulatedData();
var totals = data.GroupBy(x =>
new { x.Name, x.City, x.ZipCode }).Select(y =>
y.Sum(i => i.SomeValue));
var groupsForIterate = data.GroupBy(x =>
new { x.Name, x.City, x.ZipCode });
Console.WriteLine("Totals: ");
foreach (var total in totals)
{
Console.WriteLine(total);
}
Console.WriteLine("Categories: ");
foreach (var categ in groupsForIterate)
{
// You can refer to one field like this: categ.Key.Ciduad
Console.WriteLine("Group" + categ.Key);
Console.WriteLine(categ.Sum(x => x.SomeValue));
}
//Output:
//Totals:
//1
//2
//1
//Categories:
//Group{ Name = Mark, City = BCN, ZipCode = 00000 }
//1
//Group{ Name = Mark, City = BCN, ZipCode = 000000 }
//2
//Group{ Name = John, City = NYC, ZipCode = 000000 }
//1
}
private static List<Person> GetPopulatedData()
{
List<Person> datos = new List<Person>()
{
new Person(){Name="Mark", City = "BCN",
ZipCode = "00000", SomeValue = 1}, // group A
new Person(){Name="Mark", City = "BCN",
ZipCode = "000000", SomeValue = 1}, // group B
new Person(){Name="Mark", City = "BCN",
ZipCode = "000000", SomeValue = 1}, // group B
new Person(){Name="John", City = "NYC",
ZipCode = "000000", SomeValue = 1}, // group C
};
return datos;
}
}
Sum Ficha_Venda and Entrada from Movimento:
var query = from bd in db.Movimento
where (movimento.Data != null ? bd.Data == movimento.Data : bd.Data == movimento.Data)
&& (bd.Loja == Loja)
group bd by bd.Data into t
select new {entrada = t.Sum(bd=> bd.Entrada), ficha = t.Sum(bd=> bd.Ficha_Venda)};

Categories

Resources