I am trying to getting the max enddate of a list object using a group by with the Id and ProductId. But I am getting a Nullable object must have a value." I have tried the HasValue but I get a runtime error? Am I doing this incorrect?
static void Main(string[] args)
{
try
{
List<Data> ListOfData = new List<Data>();
ListOfData.Add(new Data() { Id = 1, ProductId = 5, EndDate = null });
ListOfData.Add(new Data() { Id = 2, ProductId = 6, EndDate = null });
ListOfData.Add(new Data() { Id = 3, ProductId = 4, EndDate = DateTime.Now });
//var ListofUniqueData = ListOfData.GroupBy(r => new { r.Id, r.ProductId })
// .Select(g => g.OrderByDescending(r => r.EndDate.HasValue ? r.EndDate.Value : null ).First())
// .Select(i => new { i.Id, i.ProductId, i.EndDate.HasValue ? i.EndDate.Value : null })
// .ToList();
var ListofUniqueData = ListOfData.GroupBy(r => new { r.Id, r.ProductId })
.Select(g => g.OrderByDescending(r => r.EndDate.Value)
.First())
.Select(i => new { i.Id, i.ProductId, i.EndDate.Value })
.ToList();
}
catch ( Exception ex )
{
ex.ToString();
}
}
class Data
{
public int Id { get; set; }
public int ProductId { get; set; }
public DateTime? EndDate { get; set; }
}
You can use GetValueOrDefault to get the default value of the type if it's not assigned:
var ListofUniqueData = ListOfData.GroupBy(r => new {r.Id, r.ProductId})
.Select(g => g.OrderByDescending(r => r.EndDate.GetValueOrDefault()).First())
.Select(i => new {i.Id, i.ProductId, i.EndDate.GetValueOrDefault()})
.ToList();
Related
I'm just wondering if there's a better way to write this code, basically the source object contains a mix of items with a boolean property however the destination object has two lists which should contain the true/false items independently.
I've written it in Linq and it works just fine but it feels as though there's a better way. Any suggestions?
void Main()
{
var s = new ResponseObject()
{
Results = new List<GroupedObject>()
{
new GroupedObject()
{
Name = "List A",
List=new List<DetailObject>()
{
new DetailObject{ Name = "Allowed", AllowedAccess = true},
new DetailObject{ Name = "Restricted", AllowedAccess = false}
}
},
new GroupedObject()
{
Name = "List B",
List=new List<DetailObject>()
{
new DetailObject{ Name = "Allowed", AllowedAccess = true},
new DetailObject{ Name = "Restricted", AllowedAccess = false}
}
}
}
};
var d = new ResponseViewModel();
d.AllowedResults = FilterObjectsByAccess(s.Results, true);
d.RestrictedResults = FilterObjectsByAccess(s.Results, false);
// Other stuff
}
public IEnumerable<GroupedObject> FilterObjectsByAccess(IEnumerable<GroupedObject> source, bool allowAccess)
{
return source.Where(i => i.List.Any(c => c.AllowedAccess == allowAccess))
.Select(i => new GroupedObject()
{
Name = i.Name,
List = i.List.Where(c => c.AllowedAccess == allowAccess)
});
}
public class ResponseObject
{
public IEnumerable<GroupedObject> Results { get; set; }
}
public class ResponseViewModel
{
public IEnumerable<GroupedObject> AllowedResults { get; set; }
public IEnumerable<GroupedObject> RestrictedResults { get; set; }
}
public class GroupedObject
{
public string Name { get; set; }
public IEnumerable<DetailObject> List { get; set; }
}
public class DetailObject
{
public string Name { get; set; }
public bool AllowedAccess { get; set; }
}
One change that may be worth benchmarking would be changing:
public IEnumerable<GroupedObject> FilterObjectsByAccess(IEnumerable<GroupedObject> source, bool allowAccess)
{
return source.Where(i => i.List.Any(c => c.AllowedAccess == allowAccess))
.Select(i => new GroupedObject()
{
Name = i.Name,
List = i.List.Where(c => c.AllowedAccess == allowAccess)
});
}
to:
public IEnumerable<GroupedObject> FilterObjectsByAccess(IEnumerable<GroupedObject> source, bool allowAccess)
{
return source
.Select(i => new GroupedObject()
{
Name = i.Name,
List = i.List.Where(c => c.AllowedAccess == allowAccess).ToList() // `ToList` here is optional - it is a trade-off between RAM and CPU
})
.Where(z => z.List.Any());
}
Your original code, with the use of Any then Where would enumerate i.List twice. The above change would likely improve that.
Another approach, which would likely involve even higher memory consumption could be to switch to using ToLookup:
var d = new ResponseViewModel
{
AllowedResults =
FilterObjectsByAccess(s.Results)
.Select(z => new GroupedObject() { Name = z.Name, List = z.GroupedList[false] })
.Where(z => z.List.Any()),
RestrictedResults =
FilterObjectsByAccess(s.Results)
.Select(z => new GroupedObject() { Name = z.Name, List = z.GroupedList[true] })
.Where(z => z.List.Any())
};
// Other stuff
}
public List<SpecialGroupedObject> FilterObjectsByAccess(IEnumerable<GroupedObject> source)
{
return source.Select(i => new SpecialGroupedObject()
{
Name = i.Name,
GroupedList = i.List.ToLookup(c => c.AllowedAccess)
}).ToList();
}
I can suggest you to use ToDictionary() like this:
var result = new[] {true, false}.ToDictionary(k => k,
v =>
s.Results.Where(w => w.List.Any(x => x.AllowedAccess == v))
.Select(c => new GroupedObject {Name = c.Name, List = c.List.Where(l => l.AllowedAccess == v)}));
var allowedResults = result[true];
var restrictedResults = result[false];
Or this:
var result = s.Results
.SelectMany(c => c.List, (b, c) => new {b.Name, DObj = c})
.GroupBy(g => g.DObj.AllowedAccess)
.ToDictionary(k=> k.Key,
c =>
new {
c.Key,
List =
c.GroupBy(cg => cg.Name)
.Select(
x => new GroupedObject {Name = x.Key, List = x.Select(l => l.DObj).ToList()})
.ToList()
});
I need to present a list of Ads grouped by category, including the Ads Count for each category.
Categories are grouped by a Parent Category like Cars that include the Categories Saloon, Cabriolet and Sports.
Models:
public class Ad
{
[Key]
public int Id { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int Id { get; set; }
[ForeignKey("CategoryParent")]
public int? CategoryParent_Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Ad> Ads { get; set; }
}
The result as to be:
Cars - Count: 100 (where 100 is the sum of for example 20 Saloon's Ads, 80 Cabrilet's)
At the moment, I'm only able to present the list of all Categories, and not grouped by Parent Category.
var adIds = {1,2,4,5}
var result =
from c in categoryQuery
let searchCount = c.Ads.Count(a => adIds.Contains(a.Id))
where searchCount > 0
select new CategoryGetAllBySearchDto
{
Id = c.CategoryParent_Id,
Name = c.CategoryParent.Name,
SearchCount = searchCount,
Ids = c.Ads.Where(a => adIds.Contains(a.Id)).Select(a => a.Id)
};
GroupBy in memory:
var adIds = { 1, 2, 4, 5 };
var result = categoryQuery.Where(c => c.Ads.Any(a => adIds.Contains(a.Id)))
.Select(c => new
{
c.CategoryParent_Id,
c.CategoryParent.Name,
Ids = c.Ads.Where(a => adIds.Contains(a.Id)).Select(a => a.Id).AsEnumerable()
})
.ToList()
.GroupBy(c => new {c.CategoryParent_Id, c.Name})
.Select(g => new CategoryGetAllBySearchDto
{
Id = g.Key.CategoryParent_Id,
Name = g.Key.Name,
Ids = g.SelectMany(u => u.Ids).AsEnumerable()
})
.ToList();
i think you need this:
var adIds = { 1, 2, 4, 5 };
var result = from c in categoryQuery
where c.Ads.Any(a => adIds.Contains(a.Id))
group c by new {c.CategoryParent_Id, c.CategoryParent.Name} into g
select new CategoryGetAllBySearchDto
{
Id = g.Key.CategoryParent_Id,
Name = g.Key.Name,
SearchCount = g.SelectMany(u => u.Ads)
.Where(a => adIds.Contains(a.Id))
.Count(),
Ids = g.SelectMany(u => u.Ads)
.Where(a => adIds.Contains(a.Id))
.Select(a => a.Id)
};
you can get out the SearchCount an add the AsEnumerable to Ids to get query just once
public class CategoryGetAllBySearchDto
{
public int? Id { get; set; }
public string Name { get; set; }
public int SearchCount { get { return this.Ids.Count() } }
public IEnumerable<int> Ids { get; set; }
}
and the query :
var adIds = { 1, 2, 4, 5 };
var result = from c in categoryQuery
where c.Ads.Any(a => adIds.Contains(a.Id))
group c by new {c.CategoryParent_Id, c.CategoryParent.Name} into g
select new CategoryGetAllBySearchDto
{
Id = g.Key.CategoryParent_Id,
Name = g.Key.Name,
Ids = g.SelectMany(u => u.Ads)
.Where(a => adIds.Contains(a.Id))
.Select(a => a.Id)
.AsEnumerable()
};
I have a table with Logs and I am counting the Logs per day as follows:
// Count logs by day
IList<DataModel> models = _context.Logs
.Where(x => x.Created >= dateMinimum && x.Created <= dateMaximum)
.GroupBy(x => new { Year = x.Created.Year, Month = x.Created.Month, Day = x.Created.Day })
.Select(x => new { Year = x.Key.Year, Month = x.Key.Month, Day = x.Key.Day, Count = x.Count() })
.AsEnumerable()
.Select(x => new DataModel { Date = new DateTime(x.Year, x.Month, x.Day), LogsCount = x.Count })
.ToList();
// Fill empty days with dates which contains all days in range
models.AddRange(dates.Where(x => !models.Any(y => y.Date == x.Date)).Select(x => new DataModel { Date = x, LogsCount = 0 }));
This is working if I want to count all logs by day independently of the type.
But I would like to count logs by day and type (Error, Warn, Info, ...).
I tried to add x.Type to group but at the end I get only 3 items.
At the moment my DataModel is the following:
public class DataModel
{
public DateTime Date { get; set; }
public Int32 LogsCount { get; set; }
}
But maybe it should be something like:
public class DataModel
{
public DateTime Date { get; set; }
public KeyValuePair<String, Int32> LogsCount { get; set; }
}
Where LogsCount has a string which holds the Type and Int32 which contains the count.
How can I do this?
Might want to consider using entity functions for grouping by date.
Example:
var results = query.GroupBy(r => new
{
SentDate = System.Data.Objects.EntityFunctions.TruncateTime(r.Launch.EmailDeliveredDate),
EventSubTypeID = r.EmailEventSubtypeID
})
.Select(x => new
{
x.Key.SentDate,
x.Key.EventSubTypeID,
NumResults = x.Count()
})
.ToList();
Did you try something like this?
IList<DataModel> models = Logs
.Where(x => x.Created >= dateMinimum && x.Created <= dateMaximum)
.GroupBy(x => new { Year = x.Created.Year, Month = x.Created.Month, Day = x.Created.Day, Type = x.Type })
.Select(x => new { Year = x.Key.Year, Month = x.Key.Month, Day = x.Key.Day, Count = x.Count(), Type = x.Key.Type })
.AsEnumerable()
.Select(x => new DataModel { Date = new DateTime(x.Year, x.Month, x.Day), LogsCount = x.Count, Type = x.Type })
.ToList()
public class DataModel
{
public DateTime Date { get; set; }
public Int32 LogsCount { get; set; }
public string Type { get; set; }
}
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
})
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)};