var users = new[]
{
new { id = 5, user_id = 3, permissions = new [] { "apple", "Pineapple", "Strawberry", "Gooseberry", "Custard", }, department_id = 10, },
new { id = 6, user_id = 3, permissions = new [] { "apple", "Pineapple", "Strawberry", "Gooseberry", "Custard", }, department_id = 11, },
new { id = 7, user_id = 3, permissions = new [] { "apple", "Pineapple", "Strawberry", "Gooseberry", "Custard", }, department_id = 12, },
new { id = 8, user_id = 5, permissions = new [] { "apple", "Pineapple", "Strawberry", "Gooseberry", "Custard", }, department_id = 10, },
new { id = 9, user_id = 6, permissions = new [] { "apple", "Pineapple", "Strawberry", "Gooseberry", "Custard", }, department_id = 11, },
new { id = 10, user_id = 7, permissions = new [] { "apple", "Pineapple", "Strawberry", "Gooseberry", }, department_id = 12, },
new { id = 11, user_id = 8, permissions = new [] { "apple", "Pineapple", "Strawberry", }, department_id = 10, },
new { id = 12, user_id = 9, permissions = new [] { "apple", "Pineapple", }, department_id = 11, },
new { id = 13, user_id = 10, permissions = new [] { "Gooseberry", "Custard", }, department_id = 12, },
new { id = 20, user_id = 11, permissions = new [] { "Raspberry", }, department_id = 15, },
};
From a list of users and their permissions, I want to find a user's manager.
The manager is a user who has the same department and permissions as the user in question, but they either have more permissions or they work across more departments.
The direct manager is a manager whose departments or permissions are the closest to the user.
for example:
user_id=8 => manager = user_id=5
user_id=5 => manager = user_id=3
user_id=3 has 3 department so he is the manager, user_id=1 has only 1 department.
user_id=11 has no manager.
user_id=5 and user_id=8 has same department but not the same permissions
user_id=10 => manager = user_id=3
user_id=7 is not his manager, they have same department but user_id=7 is missing a permission.
This seems to work:
var users = new[]
{
new { id = 5, user_id = 3, permissions = new [] { "apple", "Pineapple", "Strawberry", "Gooseberry", "Custard", }, department_id = 10, },
new { id = 6, user_id = 3, permissions = new [] { "apple", "Pineapple", "Strawberry", "Gooseberry", "Custard", }, department_id = 11, },
new { id = 7, user_id = 3, permissions = new [] { "apple", "Pineapple", "Strawberry", "Gooseberry", "Custard", }, department_id = 12, },
new { id = 8, user_id = 5, permissions = new [] { "apple", "Pineapple", "Strawberry", "Gooseberry", "Custard", }, department_id = 10, },
new { id = 9, user_id = 6, permissions = new [] { "apple", "Pineapple", "Strawberry", "Gooseberry", "Custard", }, department_id = 11, },
new { id = 10, user_id = 7, permissions = new [] { "apple", "Pineapple", "Strawberry", "Gooseberry", }, department_id = 12, },
new { id = 11, user_id = 8, permissions = new [] { "apple", "Pineapple", "Strawberry", }, department_id = 10, },
new { id = 12, user_id = 9, permissions = new [] { "apple", "Pineapple", }, department_id = 11, },
new { id = 13, user_id = 10, permissions = new [] { "Gooseberry", "Custard", }, department_id = 12, },
new { id = 20, user_id = 11, permissions = new [] { "Raspberry", }, department_id = 15, },
};
var userDepartments = users.Select(x => new { x.user_id, x.department_id }).Distinct().ToLookup(x => x.user_id, x => x.department_id);
var query =
from u in users
join m in users on u.department_id equals m.department_id
where u.user_id != m.user_id
where userDepartments[m.user_id].Count() > 1
|| (m.permissions.Except(u.permissions).Any() && !u.permissions.Except(m.permissions).Any())
select new { user = u.user_id, manager = m.user_id };
That gives me:
I'm not sure if you need a single direct linq expression to query all users, but from a first principals I've prepared a fiddle: https://dotnetfiddle.net/LvYRqS
// Get the departments of the passing user
var userPermissions = permissions.Where(x => x.user_id == userId).ToList();
var departments = userPermissions.Select(x => x.department_id).ToList();
// get all the permissions of this user
var usersPermissionValues = userPermissions.SelectMany(x => x.permissions.Split(';')).Select(x => x.Trim()).Where(x => !String.IsNullOrWhiteSpace(x)).Distinct().ToArray();
// managers or higher-ups have the same department(s) or more, and the same permissions or more
var managers = from p in permissions
where p.user_id != userId
group p by p.user_id into u
where departments.All(d => u.Any(x => x.department_id == d))
let mgrPermissions = u.SelectMany(x => x.permissions.Split(';').Select(x => x.Trim()).Where(x => !String.IsNullOrWhiteSpace(x))).Distinct().ToList()
where !usersPermissionValues.Except(mgrPermissions).Any()
select new
{
user_id = u.Key,
permissionCount = mgrPermissions.Count(),
permissions = String.Join(",", mgrPermissions),
missingPermission = usersPermissionValues.Except(mgrPermissions).Count(),
Departments = u.GroupBy(x => x.department_id).Count()
};
// The direct manager is a manager with the lowest count of departments and permissions.
var directManager = managers.OrderBy(x => x.Departments).ThenBy(x => x.permissionCount).Select(x => x.user_id).FirstOrDefault();
return directManager == 0 ? null : directManager;
Given an array of permissions we could query all the users and their direct managers: https://dotnetfiddle.net/E3iItX
IEnumerable<DepartmentUserAccess> permissions = ...
var usersAndDirectManagers =
from p in permissions
group p by p.user_id into user
let userDepartments = user.Select(x => x.department_id).ToList()
let userPermissions = user.SelectMany(x => x.permissions.Split(';').Select(x => x.Trim()).Where(x => !String.IsNullOrWhiteSpace(x))).Distinct().ToList()
select new
{
user_id = user.Key,
departments = userDepartments,
permissions = userPermissions,
manager_id = (from mgr in permissions.Where(x => x.user_id != user.Key).GroupBy(x => x.user_id)
where userDepartments.All(d => mgr.Any(x => x.department_id == d))
let mgrPermissions = mgr.SelectMany(x => x.permissions.Split(';').Select(x => x.Trim()).Where(x => !String.IsNullOrWhiteSpace(x))).Distinct().ToList()
where !userPermissions.Except(mgrPermissions).Any()
orderby mgr.Select(x => x.department_id).Count()
orderby mgrPermissions.Count()
select mgr.Key).Cast<int?>().DefaultIfEmpty((int?)null).First()
};
If you had to query this only in a specific report, then I guess it would work. The problem is that the number of permissions or even permissions itself not a very good metric for most software platforms, for this to work you are expecting all possible permissions to be expanded out for each user which is troublesome for role-based security models.
A declarative approach will make the data processing more efficient if you have to do this type of query a lot. If each department has a specific supervisor or manager, then put the supervisor FK on the department table. Otherwise user's supervisor could be defined as an FK on the user table.
Some systems will group the users into teams who have a single supervisor, this model makes it easy to swap out the supervisor whilst still retaining the rest of the team, the team can have a department FK enabling multiple teams per department, or you might even create a M:N relationship between teams and departments if you need to.
A declarative structure would also help to query the reverse, show me the users for each manager, or to build a hierarchical tree showing the CEO at the top, showing for each management level who their direct reports are. You can do it with the current structure, but it's not pretty LINQ or SQL.
Related
This is my code:
var data = context.GetAll<UserRole>()
.GroupBy(x => new RoleGroup { RoleId = x.RoleId, AccessEnum = x.AccessEnum })
.Where(x => x.Count() > 1);
This is the data mock:
var userRoles = new List<UserRole>
{
new UserRole { Id = 1, RoleId = 1, AccessEnum = AccessEnum.Full },
new UserRole { Id = 2, RoleId = 1, AccessEnum = AccessEnum.Full },
new UserRole { Id = 3, RoleId = 2, AccessEnum = AccessEnum.ReadOnly },
new UserRole { Id = 4, RoleId = 2, AccessEnum = AccessEnum.Full },
new UserRole { Id = 5, RoleId = 2, AccessEnum = AccessEnum.ReadOnly }
};
mockContext.Setup(x => x.GetAll<UserRole>()).Returns(userRoles.AsQuerable());
The GroupBy works fine when using the system. However when I run a unit test it returns each record individually. If I change the GroupBy to an anonymous object, it does work:
.GroupBy(x => new { x.RoleId, x.AccessEnum })
But I need to pass the list into another method. So I need to define the key as a RoleGroup.
Is there a way to get this working by keeping the class in the GroupBy?
I have the following three tables,
Region Table,
RegionId
RegionName
1
Admin
2
North Region
3
South Region
Branch Table
BranchId
BranchName
FkRegionId
1
Roswell Rd
3
2
Test
2
3
Piedmont Ave
2
4
Ashford Dunwoody
1
User Table
UserId
FirstName
FkBranchId
1
Hasa
9
2
Jane
1
3
Joyce
7
4
Jane
1
5
John
3
6
Sharon
8
As mentioned above, each branch has its region and each user has a branch. I need to create following JSON for my frond-end usage. Therefore I need to populate data in order to create this JSON.
Region1
Branch1
Jane
Paul
Alex
Branch2
Neil
Kaja
Alex
Region2
Branch4
Ama
Hema
Ira
Branch5
Laura
Tim
Yea
How can I do this using C# and linq?
You need to use Linq Join and GroupBy as following:
var regions = new List<Region>()
{
new Region() { RegionId = 1, RegionName = "Admin" },
new Region() { RegionId = 2, RegionName = "North Region" },
new Region() { RegionId = 3, RegionName = "South Region" }
};
var branchs = new List<Branch>()
{
new Branch() {BranchId = 1, BranchName = "Roswell Rd", FkRegionId = 3},
new Branch() {BranchId = 2, BranchName = "Test", FkRegionId = 2},
new Branch() {BranchId = 3, BranchName = "Piedmont Ave ", FkRegionId = 2},
new Branch() {BranchId = 4, BranchName = "Ashford Dunwoody ", FkRegionId = 1},
};
var users = new List<User>()
{
new User() {UserId = 1, FirstName = "Hasa", FkBranchId = 9},
new User() {UserId = 2, FirstName = "Jane", FkBranchId = 1},
new User() {UserId = 3, FirstName = "Joyce", FkBranchId = 7},
new User() {UserId = 4, FirstName = "Jane", FkBranchId = 1},
new User() {UserId = 5, FirstName = "John", FkBranchId = 3},
new User() {UserId = 6, FirstName = "Sharon", FkBranchId = 8},
};
var tree = from user in users
join branch in branchs on user.FkBranchId equals branch.BranchId
join region in regions on branch.FkRegionId equals region.RegionId
group region by new { region.RegionId, branch.BranchId } into grp
select new
{
RegionName = regions.FirstOrDefault(s => s.RegionId == grp.Key.RegionId).RegionName,
Branchs = new
{
BranchName = branchs.FirstOrDefault(s => s.FkRegionId == grp.Key.RegionId).BranchName,
Users = users.Where(i => i.FkBranchId == grp.Key.BranchId).Select(s => new
{
FirstName = s.FirstName
})
}
};
var json = JsonConvert.SerializeObject(tree, Formatting.Indented);
This will give you an expected result:
[
{
"RegionName": "South Region",
"Branchs": {
"BranchName": "Roswell Rd",
"Users": [
{
"FirstName": "Jane"
},
{
"FirstName": "Jane"
}
]
}
},
{
"RegionName": "North Region",
"Branchs": {
"BranchName": "Test",
"Users": [
{
"FirstName": "John"
}
]
}
}
]
var jsonResponse = "[{\"UserId\":195,\"FirstName\":\"Carlton\",\"BranchId\":4,\"BranchName\":\"Test\",\"RegionId\":1,\"RegionName\":\"Admin\"},{\"UserId\":223,\"FirstName\":\"Lorenza\",\"BranchId\":4,\"BranchName\":\"Test\",\"RegionId\":1,\"RegionName\":\"Admin\"},{\"UserId\":163,\"FirstName\":\"Alice\",\"BranchId\":17,\"BranchName\":\"Ratnapura\",\"RegionId\":1,\"RegionName\":\"Admin\"},{\"UserId\":264,\"FirstName\":\"Karen\",\"BranchId\":7,\"BranchName\":\"Peachtree\",\"RegionId\":3,\"RegionName\":\"South Region\"},{\"UserId\":266,\"FirstName\":\"Starla\",\"BranchId\":7,\"BranchName\":\"Peachtree\",\"RegionId\":3,\"RegionName\":\"South Region\"},{\"UserId\":30,\"FirstName\":\"Jane\",\"BranchId\":9,\"BranchName\":\"Henderson Mill\",\"RegionId\":3,\"RegionName\":\"South Region\"}]";
var myDeserializedClass = JsonConvert.DeserializeObject < List < Root >> (jsonResponse);
var jsonResponseList = myDeserializedClass.GroupBy(item = >item.RegionId).Select(grp = >grp.GroupBy(item = >item.BranchId)).Select(grp = >grp.Select(innerg = >innerg.GroupBy(item = >item.UserId))).ToList();
var serializer = JsonConvert.SerializeObject(jsonResponseList, Formatting.Indented);
Root class
public class Root
{
public int UserId { get; set; }
public string FirstName { get; set; }
public int BranchId { get; set; }
public string BranchName { get; set; }
public int RegionId { get; set; }
public string RegionName { get; set; }
}
and the output:
[
[
[
[
{
"UserId": 195,
"FirstName": "Carlton",
"BranchId": 4,
"BranchName": "Test",
"RegionId": 1,
"RegionName": "Admin"
}
],
[
{
"UserId": 223,
"FirstName": "Lorenza",
"BranchId": 4,
"BranchName": "Test",
"RegionId": 1,
"RegionName": "Admin"
}
]
],
[
[
{
"UserId": 163,
"FirstName": "Alice",
"BranchId": 17,
"BranchName": "Ratnapura",
"RegionId": 1,
"RegionName": "Admin"
}
]
]
],
[
[
[
{
"UserId": 264,
"FirstName": "Karen",
"BranchId": 7,
"BranchName": "Peachtree",
"RegionId": 3,
"RegionName": "South Region"
}
],
[
{
"UserId": 266,
"FirstName": "Starla",
"BranchId": 7,
"BranchName": "Peachtree",
"RegionId": 3,
"RegionName": "South Region"
}
]
],
[
[
{
"UserId": 30,
"FirstName": "Jane",
"BranchId": 9,
"BranchName": "Henderson Mill",
"RegionId": 3,
"RegionName": "South Region"
}
]
]
]
]
If these are physically related tables then EF has taken care of it already. Just make sure your lazy loading is ON or you can eager load data as well by using include and then display data as it is. If you want to eager load information then try this query
using(var db = new DBContext){
var result = db.region.include(x => x.branches).theninclude(x => x.users).toList();
}
result will contain all of your data then bind that data into your custom define DTOs or you can directly bind entity models into views.
Its an overview of the code you can have your own implementation.
If there is no physical relations in between tables then use join using linq here is the link you can follow how joins works in linq.
link
I have two arrayList here here:
List<ArrayList1> data = new List<ArrayList1>() {
{ id = 1, name = "John Doe", age = 20 },
{ id = 2, name = "Mae Doe", age = 17 },
{ id = 3, name = "Mark Smith", age = 35 },
{ id = 4, name = "Selena Smith", age = 15 },
{ id = 5, name = "Shane Doe", age = 26 },
{ id = 6, name = "Will Smith", age = 45 }
};
List<ArrayList2> data = new List<ArrayList2>() {
{ id = 1, address = "Singapore", phone_number = "123", store_name = "ABC" },
{ id = 4, address = "Japan", phone_number = "456", store_name = "DEF" },
{ id = 5, address = "Korea", phone_number = "789", store_name = "GHI" }
};
I want to join them, kind of left join in mysql so the result will be like this
List<ArrayListResult> data = new List<ArrayListResult>() {
{ id = 1, name = "John Doe", age = 20, address = "Singapore", phone_number = "123", store_name = "ABC" },
{ id = 2, name = "Mae Doe", age = 17 },
{ id = 3, name = "Mark Smith", age = 35 },
{ id = 4, name = "Selena Smith", age = 15, address = "Japan", phone_number = "456", store_name = "DEF" },
{ id = 5, name = "Shane Doe", age = 26, address = "Korea", phone_number = "789", store_name = "GHI" },
{ id = 6, name = "Will Smith", age = 45 }
};
The address, phone_number, store_name will be merged to List based on the matched id.
Is there easy way of merging 2 arrayList in c# based on matched id? Please help. Thanks!
You're looking for this:
result =
(
from d1 in data1
join d2 in data2 on d1.id equals d2.id into gds
from gd in gds.DefaultIfEmpty()
select new ArrayListResult()
{
id = d1.id,
name = d1.name,
age = d1.age,
address = gd?.address,
phone_number = gd?.phone_number,
store_name = gd?.store_name,
}
).ToList();
That gives me:
I'm writing a tag search in C# MVC, but I'm only able to get all the results that have one of the words. - The output should be only where all the input words matches, and exclude if e.g. 2 words are in the input, but only one of them matches.
My code so far:
List<String> list = Request["tags"].Split(' ').ToList();
KDBEntities q = new KDBEntities();
var query = (from tag in q.KDB_tags join question in q.KDB_questions on tag.question_id equals question.id where list.Any(x => x.Equals(tag.tag)) select question);
var Rquery = query.GroupBy(x => x.id).Select(grp => grp.FirstOrDefault()).ToList();
return View(Rquery);
I've been trying to figure this out for quite a while, but with no luck.
Hope this makes sense, and any of you can help me.
Tags list:
List<TagObj> tags = new List<TagObj>()
{
new TagObj() { Id = 1, QuestionId = 1, Tag = "news" },
new TagObj() { Id = 2, QuestionId = 1, Tag = "sports" },
new TagObj() { Id = 3, QuestionId = 1, Tag = "famous" },
new TagObj() { Id = 4, QuestionId = 2, Tag = "news" },
new TagObj() { Id = 5, QuestionId = 2, Tag = "sports" },
new TagObj() { Id = 6, QuestionId = 3, Tag = "news" },
new TagObj() { Id = 7, QuestionId = 4, Tag = "funny" },
};
Questions list:
List<QuestionObj> questions = new List<QuestionObj>()
{
new QuestionObj(){ QuestionId = 1, Question = "Whats up footballers?" },
new QuestionObj(){ QuestionId = 2, Question = "These are famous news?" },
new QuestionObj(){ QuestionId = 3, Question = "Read all about it?" },
new QuestionObj(){ QuestionId = 4, Question = "You know whats funny?" }
};
These are incoming tags from the request:
var incomingTags = new List<string>() { "news", "sports" };
These are the queries:
var query = from t in tags
join q in questions on t.QuestionId equals q.QuestionId
where incomingTags.Contains(t.Tag)
select new { question = q, tag = t };
var result = query.
GroupBy(g => g.question.QuestionId).
Where(g => g.ToList().Select(l => l.tag.Tag).SequenceEqual(incomingTags)).
Select(s => s.First().question).ToList();
I have a list of ids:
var IdList = new int[]{1, 2};
I also have a list of users:
var userList = new List<User>()
{
new User() {Id = 1, Name = "User1" },
new User() {Id = 1, Name = "User2" },
new User() {Id = 2, Name = "User2" },
new User() {Id = 1, Name = "User3" },
new User() {Id = 1, Name = "User4" },
new User() {Id = 2, Name = "User4" }
};
I want to get a list of users that must contain all the Ids from the IdList. So in this example I want to return User2 and User4. I've seen other subset examples that are just using Except and return boolean and even when adapting to my needs do not produce the correct results. I also saw one marked as duplicate (Similar Question) which is trying to do exactly this but did not agree that it was a duplicate and was never truly answered. I have attempted:
userList.Where(u => IdList.All(i => i == u.Id)).ToList();
that will not return anything.
Your question is a little confusing. You say you want to get a list of users that must contain all the Ids from the IdList and your expected output is User 2 and 4. That does not makes sense because your IdList has 1 and 2.
Also you have more than one record with Id 1. User1, User2 and User3 has same Id. What is the pattern to get one record from this ?
Assuming you do not have duplicate data and you want subset of items based on the items int eh idList, You can use LINQ Contains method.
var IdList = new int[]{1, 2};
var userList = new List<User>()
{
new User() {Id = 1, Name = "User1" },
new User() {Id = 2, Name = "User2" },
new User() {Id = 3, Name = "User3" },
new User() {Id = 4, Name = "User4" }
};
var subSet = userList.Where(d=>IdList.Contains(d.Id);
//subSet will have records for User1 and User2
Use Bellow 1Line linq code.
var q = userList.GroupBy(c => c.Name).Where(c => IdList.All(ccc => userList.Where(cc => cc.Name == c.Key).Any(cc => cc.Id == ccc))).ToList();
and this code return User2 and User4
Well, this is not elegant, but works:
List<string> result = new List<string>();
result = userList.Where(x => x.Id == IdList[0]).
Select(x => x.Name).ToList();
for(int i =1; i<IdList.Count();i++)
{
result = userList.Where(x => x.Id == IdList[i]).
Select(x => x.Name).ToList().
Intersect(result).ToList();
}