Grouping / Multiple grouping with LINQ - c#

I'm trying to output some data, grouped, then group it again.
Here's some example data (from db)
I'm trying to output this, grouped like so:
Best Type Name
- Discipline name
-- Result
-- Result
-- Result
CompetitorBest class looks like:
public class CompetitorBest
{
public int ResultId { get; set; }
public string BestTypeName { get; set; }
public int BestTypeOrder { get; set; }
public string DisciplineName { get; set; }
public string ResultValue { get; set; }
public string Venue { get; set; }
public DateTime ResultDate { get; set; }
}
Currently, I've got the following
var bestsGroups = from b in Model.CompetitorBests
group b by new { b.BestTypeName, b.BestTypeOrder }
into grp
orderby grp.Key.BestTypeOrder
select new
{
BestType = grp.Key.BestTypeName,
Results = grp.ToList()
};
But this obviously doesn't take into account the grouping by DisciplineName.
My output code is something like:
foreach (var bestsGroup in bestsGroups)
{
<h2>#bestsGroup.BestType</h2>
foreach (var result in bestsGroup.Results)
{
//i am guessing here i'll need another foreach on the discipline group....
<p>#result.DisciplineName</p>
<p>#result.ResultId </p>
}
}

I think this is what you are looking for:
from b in Model.CompetitorBests
group b by new { b.BestTypeName, b.BestTypeOrder } into grp
orderby grp.Key.BestTypeOrder
select new
{
BestType = grp.Key.BestTypeName,
Results = from d in grp
group d by d.DisciplineName into grp2
select new
{
DisciplineName = grp2.Key,
Results = grp2
}
};
Edit:
Iterate over it like this:
foreach (var bestsGroup in bestsGroups)
{
<h2>#bestsGroup.BestType</h2>
foreach (var discipline in bestsGroup.Results)
{
<p>#discipline.DisciplineName</p>
foreach (var result in discipline.Results)
{
<p>#result.ResultId</p>
}
}
}

Related

Add values to a list inside a list Linq

I am having a class like this.
public class CameraModel
{
public int JobId { get; set; }
public int ViewId { get; set; }
public Guid ViewGuid { get; set; }
public string Name { get; set; }
public int ViewNum { get; set; }
public int LayoutID { get; set; }
public List<CameraViewItemModel> CameraViewItems { get; set; }
}
The CameraViewItemModel class is like this:
public class CameraViewItemModel
{
public int JobID { get; set; }
public Guid ViewGuid { get; set; }
public int ViewID { get; set; }
public int CamNum { get; set; }
public Guid ChannelGuid { get; set; }
public string Name { get; set; }
public ActionType Action { get; set; }
}
Now, I am assigning the list of CameraViewItemModel like this:
// get all the cameramodel's
cameraModels = _unitOfWork.Context.CameraViews.Where(m => m.JobId == siteId)
.Select(m => new CameraModel
{
JobId = m.JobId,
ViewId = m.ViewId,
ViewGuid = m.ViewGuid,
Name = m.Name,
ViewNum = m.ViewNum,
LayoutID = m.LayoutId
}).ToList();
// get all the cameraviewitemmodels
cameraViewItemModels =
(from cameraView in _unitOfWork.Repository<CameraViews>().Get(x => x.JobId == siteId).Result
join cameraViewItem in _unitOfWork.Repository<CameraViewItems>().Get(x => x.JobId == siteId)
.Result on cameraView.ViewId equals cameraViewItem.ViewId into CameraViewItemResults
from cameraViewItemResult in CameraViewItemResults.DefaultIfEmpty()
join cameraChannel in _unitOfWork.Repository<CameraChannels>().Get(x => x.JobId == siteId)
.Result on (cameraViewItemResult == null ? new Guid() : cameraViewItemResult.ChannelGuid) equals cameraChannel.ChannelGuid into CameraChannelResults
from cameraChannelResult in CameraChannelResults.DefaultIfEmpty()
select new CameraViewItemModel
{
JobID = cameraView.JobId,
ViewID = cameraView.ViewId,
ViewGuid = cameraView.ViewGuid,
CamNum = cameraViewItemResult.CamNum,
ChannelGuid = cameraChannelResult.ChannelGuid,
Name = cameraChannelResult.Name
}).ToList();
// then do a 'join' on JobId, ViewId and ViewGuid and assign the list of cameraviewitemmodels to cameraModels.
foreach (var cameraModel in cameraModels)
{
cameraModel.CameraViewItems = (from cameraViewItem in cameraViewItemModels
where cameraModel.JobId == cameraViewItem.JobID
&& cameraModel.ViewId == cameraViewItem.ViewID
&& cameraModel.ViewGuid == cameraViewItem.ViewGuid
select cameraViewItem).ToList();
}
return cameraModels;
There are three tables in database:
CameraViews, CameraViewItems, CameraChannels.
CameraViews is the main table. It is left joined with CameraViewItems and CameraChannels to get the desired result. There may not be any data in CameraViewItems and CameraChannels for a corresponding CameraView.
Is it possible to assign the list of CameraViewItemModels to CameraModels in a single linq statement.
Here is a simple way to add values to a sub list, dunno if this is what you mean. You can keep selecting sub lists if that is necessary.
var parent_lst = new List<List<string>>(); // Root/parent list that contains the other lists
var sub_lst = new List<string>(); // Sub list with values
var selected_parent_lst = parent_lst[0]; // Here I select sub list, in this case by list index
selected_parent_lst.Add("My new value"); // And here I add the new value

Adding multimap children into parent with Dapper

I have a very simple test project where I am trying to figure out how to add children into the parent's collection.
The datamodel is quite basic:
Current result returns a duplicated entry.
Expected/desired
I expected the result to be just one entry with two children
GROUP1 -> { USER_1, USER_2 }
GROUP class
public class GROUP
{
public GROUP()
{
this.USERs = new HashSet<USER>();
}
public int Group_ID { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public ICollection<USER> USERs { get; set; }
}
USER class
public class USER
{
public int User_ID { get; set; }
public int Group_ID { get; set; }
public string Name { get; set; }
public Nullable<int> Age { get; set; }
public GROUP GROUP { get; set; }
}
Dapper method
public GROUP Get(int id)
{
string sqlGetGroupExtended = $"SELECT _group.Group_ID, _group.Name, _group.Location, _user.User_ID, _user.Name, _user.GROUP_ID, _user.Age FROM dbo.[GROUP] _group " +
"LEFT JOIN dbo.[USER] _user ON _group.Group_ID = _user.Group_ID " +
"WHERE _group.Group_ID = #groupid;";
GROUP result = null;
var lookup = new Dictionary<int, GROUP>();
using (var connection = new SqlConnection(Properties.Settings.Default.CodeTest_DB))
{
var extendedGroup = connection.Query<GROUP, USER, GROUP>(sqlGetGroupExtended, (parent, child) =>
{
if (!lookup.TryGetValue(parent.Group_ID, out GROUP found))
{
lookup.Add(parent.Group_ID, found = parent);
}
found.USERs.Add(child);
return found;
}, param: new { groupid = id }, splitOn: "Location");
// result = extendedGroup <--- WHAT TO DO HERE?
}
return result;
}
How can I achieve this?
References:
http://dapper-tutorial.net/dapper
https://github.com/StackExchange/Dapper/blob/master/Dapper.Tests/MultiMapTests.cs#L12
If you're using SQL Server 2016 or Azure SQL you can take advance of JSON to return a hierarchical object:
https://medium.com/dapper-net/one-to-many-mapping-with-dapper-55ae6a65cfd4
It's an article I wrote on the subject, along with source code, that shows how to elegantly solve the problem.
My bad, as the code shows here https://github.com/StackExchange/Dapper/blob/master/Dapper.Tests/MultiMapTests.cs#L12
I was missing the .Distinct()
var extendedGroup = connection.Query<GROUP, USER, GROUP>(sqlGetGroupExtended, (parent, child) =>
{
if (!lookup.TryGetValue(parent.Group_ID, out GROUP found))
{
lookup.Add(parent.Group_ID, found = parent);
}
found.USERs.Add(child);
return found;
}, param: new { groupid = id }, splitOn: "Location,User_ID").Distinct();

Sort and allocate objects in a list

class user
{
public string userID { get; set; }
public string groupID { get; set; }
public int individualCredit { get; set; }
public int groupCredit { get; set; }
}
I have a list like this
List<user> listUsers = new List<user>();
I want to group users with same groupID.
Calculate groupCredit by adding the individualcredit s of each member in group and by dividing it by the number of group members.
Finally I want to assign each user with their groupCredit.
There are groups with three to five members.
How to do that?
Here's what I have tried.
var groupedUsers = from users in listUsers
where users.groupID != ""
group users by users.groupID into grouphold
select grouphold ;
Here's what I don't know.
How to search for the same groupID? In what I have tried I check for all Items groupID is blank.
Althought I agree with Sahil's comment, here is a starting point...
You can use LINQ, something like this:
List<user> listUsers = new List<user>();
public int groupUsersbyID(int _groupID)
{
var ListOfUsers = (from g in listUsers where g.groupID == _groupID select g).ToList();
var count = ListOfUsers.Count();
var totalCreditforGroup = 0;
var GroupCredit = 0;
foreach (var indidCredit in ListOfUsers)
{
totalCreditforGroup = totalCreditforGroup + indidCredit.individualCredit;
GroupCredit = totalCreditforGroup / count;
}
return GroupCredit;
}
public int returnGroupID(int userId)
{
var GroupIDforUser = (from i in listUsers where i.userID == userId select i.groupID).FirstOrDefault();
return GroupIDforUser;
}
public class user
{
public int userID { get; set; }
public int groupID { get; set; }
public int individualCredit { get; set; }
public int groupCredit { get; set; }
}

Join two list on a specified column

I am attempting to join two lists (flist and slist) on the ID column. List definitions, class definitions, list contents, and desired results are displayed below.
List<first> flist= new List<first>();
List<second> slist= new List<second>();
public class first
{
public string name { get; set; }
public int ID{ get; set; }
public string itemAttr { get; set; }
}
public class second
{
public int ID{ get; set; }
public string itemAttr{ get; set; }
}
List contents
flist:
apples | 1
bananas| 2
trees | 3
slist:
1 | fruit
3 | not-fruit
Desired result:
flist:
apples | 1 | fruit
bananas | 2 |
trees | 3 | not-fruit
List<first> flist= new List<first>();
List<second> slist= new List<second>();
var result = from f in flist
join s in slist on f.ID equals s.ID into g
select new {
f.name,
f.ID,
itemAttr = g.Any() ? g.First().itemAttr : null
};
Try this
foreach(var f in first)
{
foreach(var s in second)
{
if(f.ID == s.ID)
{
f.fAttr = item.itemAtrr;
}
}
}
You can use a "left-outer" join with Linq:
var joined = from l1 in flist
join l2 in slist on l1.ID equals l2.ID into gj
from l2_sub in gj.DefaultIfEmpty()
select new {
name = l1.name,
ID = l1.ID,
itemAttr = l2_sub == null ? String.Empty : l2_sub.itemAttr
};
namespace WindowsFormsApplication26
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
List<first> flist = new List<first>();
List<second> slist = new List<second>();
flist.Add(new first("apples", 1));
flist.Add(new first("bananas", 2));
flist.Add(new first("trees", 3));
slist.Add(new second(1, "Fruit"));
slist.Add(new second(2, ""));
slist.Add(new second(3, "Not-Fruit"));
var result = (from t in flist
join x in slist
on t.ID equals x.ID
select new
{
Name = t.name,
Id = t.ID,
attrib = x.itemAttr
}).ToList();
}
public class first
{
public first()
{
}
public first(string n, int id)
{
name = n;
ID = id;
}
public string name { get; set; }
public int ID { get; set; }
}
public class second
{
public second()
{
}
public second(int id, string itm)
{
ID = id;
itemAttr = itm;
}
public int ID { get; set; }
public string itemAttr { get; set; }
}
}
}
What's the problem? Create a new class which third with parameters: ID, name, itemAttr. Create new List with this class and will write in needed position needed data.

LINQ - GroupBy and project to a new type?

I have a list of items, i.e, List<SearchFilter>, and this is the SearchFilter object:
public class SearchFilter
{
public int ItemID { get { return ValueInt("ItemID"); } }
public string ItemName { get { return ValueString("ItemName"); } }
public string Type { get { return ValueString("Type"); } }
}
How do I group by the Type, and project the grouped item into a new list of GroupedFilter, i.e:
public class Filter
{
public int ItemID { get; set; }
public string ItemName { get; set; }
}
public class GroupedFilter
{
public int Type { get; set; }
public List<Filter> Filters { get; set; }
}
Thanks.
var result = items.GroupBy(
sf => sf.Type,
sf => new Filter() { ItemID = sf.ItemID, ItemName = sf.ItemName },
(t, f) => new GroupedFilter() { Type = t, Filters = new List<Filter>(f) });
But you need to make sure your GroupedFilter.Type property is a string to match your SearchFilter.Type property.
With Linq query syntax it is longer and more complex but just for reference:
var grpFilters = (from itm in list group itm by itm.Type into grp select
new GroupedFilter
{
Type = grp.Key,
Filters = grp.Select(g => new Filter
{
ItemID = g.ItemID,
ItemName = g.ItemName
}).ToList()
}).ToList();
Somebody may find it more readable because they don't know all the possible parameters to GroupBy().

Categories