I spent a lot of time on this problem. I am able to do simple Group By LINQ queries (on one property) but for multiple fields I'm a little stuck...
Here is a LINQPad sample of what I want to do :
dim lFinal={new with {.Year=2010, .Month=6, .Value1=0, .Value2=0},
new with {.Year=2010, .Month=6, .Value1=2, .Value2=1},
new with {.Year=2010, .Month=7, .Value1=3, .Value2=4},
new with {.Year=2010, .Month=8, .Value1=0, .Value2=1},
new with {.Year=2011, .Month=1, .Value1=2, .Value2=2},
new with {.Year=2011, .Month=1, .Value1=0, .Value2=0}}
Dim lFinal2 = From el In lFinal
Group el By Key = new with {el.Year,el.Month}
Into Group
Select New With {.Year = Key.Year, .Month=Key.Month, .Value1 = Group.Sum(Function(x) x.Value1), .Value2 = Group.Sum(Function(x) x.Value2)}
lFinal.Dump()
lFinal2.Dump()
The lFinal list has 6 items, I want the lFinal2 to have 4 items : 2010-6 and 2011-1 should group.
Thanks in advance.
Make the properties in the anonymous type immutable using the Key keyword and then they will be used for comparisons
Dim lFinal2 = From el In lFinal
Group el By Key = new with {key el.Year, key el.Month}
Into Group
Select New With {
.Year = Key.Year,
.Month = Key.Month,
.Value1 = Group.Sum(Function(x) x.Value1),
.Value2 = Group.Sum(Function(x) x.Value2)
}
Thanks !
But I noticed I needed to also write the GetHashCode function to make it works. I provide the VB.Net translation of the final class + LINQ GroupBy :
Class :
Public Class YearMonth
Implements IEquatable(Of YearMonth)
Public Property Year As Integer
Public Property Month As Integer
Public Function Equals1(other As YearMonth) As Boolean Implements System.IEquatable(Of YearMonth).Equals
Return other.Year = Me.Year And other.Month = Me.Month
End Function
Public Overrides Function GetHashCode() As Integer
Return Me.Year * 1000 + Me.Month * 100
End Function
End Class
And the LINQ query :
Dim lFinal2 = From el In lFinal
Group el By Key = New YearMonth With {.Year = el.Year, .Month = el.Month}
Into Group
Select New ItemsByDates With {.Year = Key.Year,
.Month = Key.Month,
.Value1 = Group.Sum(Function(x) x.Value1),
.Value2 = Group.Sum(Function(x) x.Value2)}
Not 100% sure but group by probably uses the Equals() and/or GetHashCode implementation, so when you do the implicit creation:
= Group el By Key = new with {el.Year,el.Month}
the implicit object doesn't know to check both year and month (just because it has the properties doesn't mean it checks them when comparing to other objects).
So you'll probably need to do something more like this:
= Group el By Key = new CustomKey() { Year = el.Year, Month = el.Month };
public class CustomKey{
int Year { get; set; }
int Month { get; set; }
public override bool Equals(obj A) {
var key (CustomKey)A;
return key.Year == this.Year && key.Month == this.Month;
}
}
Related
Hello I have a query and I want the result into list of objects, not entity. But the result is actualy a object which I should transfer to my object. Is there a way to map it directly to my custom object?
Maybe this will help you :
public interface ObjRepository extends JpaRepository<MyObject, Long> {
#Query(value = "FROM MyObject WHERE objname = ?1")
public List<MyObject> findByName(String name);
}
Approach-1: using List of object array.
When using native query, we get list of Object array i.e each row in list is array. Elements in array represent column values.
In repo interface:
#Query(value = "select col1, col2, col3 from table1 where col1 = :key", nativeQuery = true)
List<Object[]> findByKey(#Param("key") String key);
In caller
List<Object[]> objectList = new ArrayList<Object[]>();
objectList = repo.findByKey(key);
List<CustomObject> customObjectList = new ArrayList<>();
for (Object[] tuple : objectList) {
String col1 = (String) tuple[0];
String col2 = (String) tuple[1];
String col3 = (String) tuple[2];
CustomObject obj = new CustomObject();
obj.setCol1(col1);
obj.setCol2(col2);
obj.setCol3(col3);
customObjectList.add(obj);
}
return customObjectList;
Approach-2: using custom dto that represents columns in each row.
Refer https://stackoverflow.com/a/42905382/1358551
final List<MyCustomDTO> statuses = myRepository
.findStatuses(marketId, campaignId, stationIds).stream()
.map(o -> new MyCustomDTO(((BigInteger) o[0]), (Boolean) o[1], (Timestamp) o[2]))
.collect(toList());
public class StationStatusDTO {
private long id;
private boolean isSomething;
private LocalDateTime date;
public MyCustomDTO(BigInteger id, Boolean isSomething, Timestamp date) {
this(id.longValue(),
isSomething,
(date == null) ? null : LocalDateTime
.ofInstant(Instant.ofEpochMilli(date.getTime()),
TimeZone.getDefault().toZoneId()));
}
The list of doubles SC is of same size for all of CTable.
I would like add SC grouped by CTable.Ch. The result should be a List of sums. Is there an operator/customization for that?
Public Class SCRow
Public L As String
Public Ch As String
Public SC As List(Of Double)
End Class
Dim CTable AS New List(Of SCRow)
Dim AggrC =
From C In CTable
Group C By Key = C.Ch
Into Group
Select New With
{
.Ch= Key,
.SumSC = Group.Sum(Function(x) ???)
}
Example:
CTable.Add(New SCRow With {.L = "L1", .Ch = "Ch1", .SC = New List(Of Double)(New Double() {1.0, 2.0})})
CTable.Add(New SCRow With {.L = "L1", .Ch = "Ch2", .SC = New List(Of Double)(New Double() {3.0, 4.0})})
CTable.Add(New SCRow With {.L = "L2", .Ch = "Ch1", .SC = New List(Of Double)(New Double() {5.0, 6.0})})
Output:
Ch1 {6=1+5, 8=2+6}
Ch2 {3, 4}
It can be done using Aggregate extension method along with Zip method. I have written the code in C#. Please translate it to VB.Net. Hope it will help.
Aggregate: Performs a specified operation to each element in a collection, while carrying the result forward.
Zip: The Zip extension method acts upon two collections. It processes each element in two series together.
public class SCRow
{
public string L { get; set; }
public string CH { get; set; }
public List<double> SC { get; set; }
}
class Program
{
static void Main(string[] args)
{
var CTable = new List<SCRow>{
new SCRow { L = "L1", CH = "Ch1", SC = new List<double> { 1.0, 2.0}},
new SCRow { L = "L1", CH = "Ch2", SC = new List<double> { 3.0, 4.0}},
new SCRow { L = "L2", CH = "Ch1", SC = new List<double> { 5.0, 6.0}},
};
var result = CTable.GroupBy(c => c.CH)
.Select(x => new
{
Value = x.Aggregate((curr, next) => new SCRow
{
CH = x.Key,
SC = curr.SC.Zip(next.SC, (n1, n2) => n1 + n2).ToList()
})
})
.Select(y => new {
CH = y.Value.CH,
ScSum = y.Value.SC
}).ToList();
}
}
I have a Real Estate site with a Properties table and a PropertyImages table.
When the user uploads a picture, I want to run a query in the PropertyImages. then append that number to the picturename.
public IQueryable GetPictureCount()
{
int propertyId = Convert.ToInt16(ddlSelectProperty.SelectedValue);
var _db = new RESolution.Models.PropertyContext();
IQueryable query = _db.PropertyImages;
var mypic = (from c in _db.PropertyImages
where c.PropertyID == propertyId
select c).FirstOrDefault();
lblCount.Text = Convert.ToString(query);
}
I get this error: "Not all code paths return a value"
My environment is as follows:
VS Express 2013
Sql Express
win 8.1 development computer
when I change IQuaryable to "void" I get the error
'lblCount.text = query.Count().ToString();'
System.Linq.IQueryable does not contain a definition for 'Count' I have looked for a using directive but found non
I am still a little confused, though you guys are a god send.
Here is where I was able to get to work.
public void GetPictureCount()
{
lblfnameCheck.Text = ddlSelectProperty.SelectedValue;
int propertyId = Convert.ToInt32(lblfnameCheck.Text);
var _db = new RESolution.Models.PropertyContext();
var count = _db.PropertyImages.Count(t => t.PropertyID == propertyId);
lblCount.Text = Convert.ToString(count);
}
As dasblinkenlight mentioned, it seems that you do not want to return something, so make the method a void.
You probable want something like this:
public void GetPictureCount()
{
int propertyId = Convert.ToInt16(ddlSelectProperty.SelectedValue);
var _db = new RESolution.Models.PropertyContext();
IQueryable query = _db.PropertyImages;
var mypic = (from c in _db.PropertyImages
where c.PropertyID == propertyId
select c).FirstOrDefault();
var nrOfPics = Convert.ToString(query.Count());
lblCount.Text = nrOfPics;
mypic.name = mypic.name + nrOfPics; // I am guessing the name property
_db.SaveChanges();
}
I have a database table. What I want is to get data using group by clause as I have used in below code.
Note that Decision is another table. now I want that all the decisions related to a specific Meeting Title should be shown in list.like
meetingtitle1=decision1,decison2,decison3
meetingtitle2=decision1,decison2
but below code returns only one decisiontitle.
public List<NewMeetings> GetAllMeetings()
{
var xyz = (from m in DB.MeetingAgenda
//join mp in Meeting on m.MeetingId equals mp.MeetingId
//where m.MeetingId == 2
group m by new { m.Meeting.MeetingTitle } into grp
select new NewMeetings
{
// meetingid = grp.Key.MeetingId,
meetingtitle = grp.Key.MeetingTitle,
decision = grp.Select(x => x.Decision.DecisionTitle).FirstOrDefault(),
total = grp.Count()
}).ToList();
List<NewMeetings> list = xyz.ToList();
return list;
}
public class NewMeetings
{
public int meetingid;
public string meetingtitle;
public string decision;
public int total;
}
Can somebody please tell me how to return a list of decisions to a specific Meetingtitle?
You are doing a FirstOrDefault on the list of decisions which obviously means you are only getting a single value. Instead you can join them all together into one longer string (separated by commas as you indicated in the question) by changing this line:
decision = grp.Select(x => x.Decision.DecisionTitle).FirstOrDefault(),
To this:
decision = string.Join(",", grp.Select(x => x.Decision.DecisionTitle)),
However, as the string.Join is not recognised by Linq to Entities, you need to do the string.Join after the data has been retrieved (i.e. after the ToList):
var xyz = (from m in DB.MeetingAgenda
group m by new { m.Meeting.MeetingTitle } into grp
select new
{
meetingtitle = grp.Key.MeetingTitle,
decisions = grp.Select(x => x.Decision.DecisionTitle),
total = grp.Count()
})
.ToList()
.Select(m => new NewMeetings
{
meetingtitle = m.meetingtitle,
decision = string.Join(",", m.decisions),
total = m.total
});
I have this model:
Public Class Tbl_Exercise
<Key()> Public Property Exercise_ID() As Integer
Public Property Exercise_Employee_ID() As Integer
Public Property Exercise_Create_Date() As Date
<ForeignKey("Tbl_Exercise_Type")> _
Public Property Exercise_Type_ID() As Integer
Public Property Exercise_Duration() As Integer
Public Overridable Property Tbl_Exercise_Type As Tbl_Exercise_Type
End Class
I need to get the sum of the Exercise_Duration for each week of the year. I need to then check if the sum for the week is greater than or equal to 150. If it is, I need to +1 another variable (a count). The goal is to display this:
# of weeks you've reached 150: X out of Z
(Where X is the count of weeks greater than or equal to 150 and Z is equal to the total number of weeks in the current year.)
Final
' get number of weeks the exercise goal was reached (greater than or equal to the goal)
Dim exerciseDb = New ExerciseDbContext
Dim exercise = exerciseDb.Tbl_Exercises.Where(Function(x) x.Exercise_Employee_ID = empId)
Dim weeks = exercise.ToList.GroupBy(Function(x) CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(x.Exercise_Create_Date, CalendarWeekRule.FirstDay, DayOfWeek.Sunday))
Dim totalWeeks = 0
For Each week In weeks
Dim sum = week.Sum(Function(x) x.Exercise_Duration)
If sum > 150 Then
totalWeeks += 1
End If
Next
Debug.Print("over150: " + totalWeeks.ToString)
using System.Globalization;
DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo;
Calendar cal = dfi.Calendar;
var recap =
(from e in exercises
group e by cal.GetWeekOfYear(e.Exercise_Create_Date,
dfi.CalendarWeekRule,
dfi.FirstDayOfWeek)
into g
select new
{
g.Key,
Total = g.Sum(x => x.Exercise_Duration)
}
into p
where p.Total > 150
select p)
.Count();
Here is an example in C#:
public class Exercise
{
public DateTime CreateDate { get; set; }
public int Duration { get; set; }
}
class Program
{
static void Main()
{
Exercise[] ex = new Exercise[]
{
new Exercise { CreateDate = DateTime.Parse("1/1/2012"), Duration = 160 },
new Exercise { CreateDate = DateTime.Parse("1/8/2012"), Duration = 160 },
new Exercise { CreateDate = DateTime.Parse("1/15/2012"), Duration = 160 },
new Exercise { CreateDate = DateTime.Parse("2/1/2012"), Duration = 100 },
new Exercise { CreateDate = DateTime.Parse("3/1/2012"), Duration = 75 },
new Exercise { CreateDate = DateTime.Parse("3/1/2012"), Duration = 80 }
};
var weeks = ex.GroupBy(x => CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(x.CreateDate, CalendarWeekRule.FirstDay, DayOfWeek.Sunday));
int currentweek = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
int over150 = weeks.Where(group => group.Sum(item => item.Duration) > 150).Count();
Console.WriteLine(String.Format("# of weeks you've reached 150: {0} out of {1}", over150, currentweek));
}
}