Is there a better LINQ Query to do this? - c#

Lets say I have a flat list of objects, each of which has a person's name, and a single Role they're in, like so:
var People = new[] {
new PersonRole(){ Name = "Adam", Role = "R1" },
new PersonRole(){ Name = "Adam", Role = "R2" },
new PersonRole(){ Name = "Adam", Role = "R3" },
new PersonRole(){ Name = "Bob", Role = "R1" },
};
Now, is there a direct way to get this into a Dictionary<string, List<string>> based on Name and Role (obviously). I did it in two steps, like below, but I have to think there's a more direct way.
Dictionary<string, List<string>> resultLookup =
People.Select(p => p.Name).Distinct().ToDictionary(str => str, str => new List<string>());
People.ForEach(p => resultLookup[p.Name].Add(p.Role));
Thanks!

Dictionary<string, List<string>> dictionary = people.GroupBy(p => p.Name).ToDictionary(g => g.Key, g => g.Select(p => p.Role).ToList());
Untested, but I think that should work...
EDIT Tested it now, and that does in fact work.

This should do it:
var people = new[] {
new { Name = "Adam", Role = "R1" },
new { Name = "Adam", Role = "R2" },
new { Name = "Adam", Role = "R3" },
new { Name = "Bob", Role = "R1" },
};
var r = from p in people
group p by p.Name
into g
select new {
Name = g.Key,
Roles = g.Select(p => p.Role).ToList()
};
var d = r.ToDictionary(k => k.Name, e => e.Roles);

var dict = People.ToLookup(p=>p.Name, p=>p.Role)
.ToDictionary(it=>it.Key, it=>it.ToList());

var result = People.GroupBy(a => a.Name).ToDictionary(a => a.First().Name, b => b.Select(c => c.Role).ToList());

People
.GroupBy( p => p.Name )
.ToDictionary( p => p.Key, p => p.Select(pr => pr.Role).ToList() )

Related

How to create a moq unit test when grouping by class

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?

filter on CollectionProperties gives me all 4 records instead of 2

I have a list lstCollectionInstances which has 4 collection instance,
var lstCollectionInstances = new List<CollectionInstance>
{
new CollectionInstance
{
Name = "A",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P1", Value = 10, DataType = "D"}
}
},
new CollectionInstance
{
Name = "A",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P2", Value = "H1", DataType = "S"}
}
},
new CollectionInstance
{
Name = "B",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P1", Value = 20, DataType = "D"}
}
},
new CollectionInstance
{
Name = "B",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P2", Value = "H2", DataType = "S"}
}
},
};
Now when I filter it based on CollectionProperty data types D it's should give me 2 records, but below code giving me all 4 records, what is missing here?
var X = lstCollectionInstances.Select(x => new CollectionInstance
{
Name = x.Name,
CollectionProperties = x.CollectionProperties.Where(cp => cp.DataType == "D").ToList()
}).ToList();
This one selects all instances with a property of type D.
var result= lstCollectionInstances
.Where(x => x.CollectionProperties.Any(y => y.DataType == "D"))
.ToList();
It is because you're executing Select in each item of lstCollectionInstances and Where in CollectionProperties. It will return 4 items which 2 of them have empty CollectionProperties. You should execute Where first like:
var X = lstCollectionInstances.Where(a => a.CollectionProperties.Any(cp => cp.DataType == "D")).Select(x => new CollectionInstance
{
Name = x.Name,
CollectionProperties = x.CollectionProperties
}).ToList();

Return List that Contains All Ids from an int list

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();
}

MVC - Ordered List

I am trying to order the following list by descending but I can't seem to figure out how it is done:
var cust = item.Profiles.Select(c => new
{
id = c.CustId,
Name = c.Name
}).ToList();
ViewBag.Customers = new MultiSelectList(cust, "id", "Name");
This is what I have already tried:
var cust = item.Profiles.Select(c => new
{
id = c.CustId,
Name = c.Name.OrderByDescending();
}).ToList();
ViewBag.Customers = new MultiSelectList(cust, "id", "Name");
This is how the list is displayed on my view:
#Html.DropDownList("id", (MultiSelectList)ViewBag.Customers, new { #class = "form-control", id = "lstCustomer" })
Note: I am trying to sort the list in alphabetical order
var cust = item.Profiles.Select(c => new
{
id = c.CustId,
Name = c.Name
}).OrderByDescending(c=>c.Name).ToList();
Or
var cust = item.Profiles.OrderByDescending(a=>a.Name).Select(c => new
{
id = c.CustId,
Name = c.Name
}).ToList();
Using Linq:
var cust = item.Profiles.Select(c => new
{
id = c.CustId,
Name = c.Name
}).OrderByDescending(c => c.Name).ToList();
Or More Elegant (Query Syntax):
var cust = (from c in item.Profiles
orderby c.Name
select new
{
id = c.CustId,
Name = c.Name
}).ToList();
To do the same thing with query syntax:
var cust = (from c in item.Profiles
orderby c.Name descending
select new
{
id = c.CustId,
Name = c.Name
}
).ToList();

Dynamic linq query with nested groups

I'm trying to create nested group with dynamic query.
Following are my collection of data
var invoices = new List < Purchase > () {
new Purchase() {
Id = 1, Customer = "a", Date = DateTime.Parse("1/1/2009")
}, new Purchase() {
Id = 2, Customer = "a", Date = DateTime.Parse("1/2/2009")
}, new Purchase() {
Id = 3, Customer = "a", Date = DateTime.Parse("1/2/2009")
}, new Purchase() {
Id = 4, Customer = "b", Date = DateTime.Parse("1/1/2009")
}, new Purchase() {
Id = 5, Customer = "b", Date = DateTime.Parse("1/1/2009")
}, new Purchase() {
Id = 6, Customer = "b", Date = DateTime.Parse("1/2/2009")
}
};
This linq query is returning the desired result.
var tree = invoices.GroupBy(x => x.Date).Select(x => new
{
Key = x.Key,
Items = x.GroupBy(y => y.Customer).Select(y => new
{
Key = y.Key,
Items = y
})
}).ToList();
Below is the output of the above linq query
But I just need to group different columns in different order.
So that I try to create dynamic linq query. But my code block result not same as my previous linq query.
var groupedInvoiceItems = invoices.AsQueryable().GroupBy("new (Date, Customer)", "it");
You could do this with Generics. Just Pass in your Lambda to a generic method.
Something like:
private IEnumerable<PurchaseGrp> BuildList<TSource>(IQueryable<TSource> allRecords,
Func<TSource, string> selector)
{
var result = allRecords.GroupBy(x = > x.selector(x));
return result;
}
The return type could be a new Grouped type PurchaseGrp or the same as your source (Purchase).

Categories

Resources