linq select from list remove other rows where condition - c#

I have a list like this
public class RolesAccessModel
{
public int Id { get; set; }
public string PrimaryMenu { get; set; }=string.Empty;
public string OptionMenu { get; set; } = string.Empty;
public string Roles { get; set; } = string.Empty;
public string UserType { get; set; } = string.Empty;
}
The data I have in the list is like this
1,'PR','A','R','U'
2,'PR','A','R','U2'
3,'PR','A','R1','U5'
4,'PR','A','R1','U3'
5,'PR','A','R2','U6'
6,'PR','A','R2','U4'
I have 2 or more UserType values for the shortlist (PrimaryMenu, OptionMenu, Role)
I need to retrieve the shortlist (PrimaryMenu, OptionMenu, Role) where UserType is different from a specific value...
Using Linq I have this
List<RolesAccessModel> result = roleAccess.Where(p => p.UserType!= 'U').ToList();
It retrieves all list except ID=1
But I need to remove also the shortlist (PrimaryMenu, OptionMenu, Role) of that UserType. So ID=2 also has to be remove.
One approach is to get a list that contains the ID I want to delete, and then Exclude from the first list... the second list.
But I would like to do in just Linq operation...
It is that possible?

Try:
List<RoleAccessModel> result = roleAccess.Where(p =>
p.UserType != 'U'
&& !roleAccess
.Where(p2 => p2.UserType == 'U')
.Select(p2 => p2.Roles)
.Contains(p.Roles)).ToList();

I'd approach this with grouping:
roleAccess
.GroupBy(p => (p.PrimaryMenu, p.OptionMenu, p.Roles))
.Where(g => !g.Any(p => p.UserType == "U"))
.SelectMany(g => g)
.ToList();

Related

Count the number of element present in the inner list

public Class Users
{
string UserName,
List<UserDesktop> userDesktop
}
public class UserDesktop
{
int ID,
string DesktopName,
string type -- here Type value is A, B;
}
Here I have to find the users count having desktop assigned of type A
Users variable containing all data.
List<Users> finalUsers = users.Count(s=>s.userDesktop.where(x=>x.type == "A"))
Please let me know the correct way of doing it.
You might want to use SelectMany to flatten the list then count it:
int numberOfUsersWithDesktopOfTypeA = users.SelectMany(u => u.userDesktop)
.Count(ud => ud.type == "A");
If you want the list of users that have an A desktop:
var usersWithDesktopA = Users.Where(u => u.Any(ud => ud.type == "A"));
var numberOfUsersWithDesktopA = usersWithDesktopA.Count();
To query the user(s) who has UserDesktop of type 'A':
List<Users> hasDesktopAUsers = users.Where(u => u.userDesktop.Any(ud => ud.type == "A"))
.ToList();
Next, you can get the number of count from the previous result as:
int numberOfDesktopAUsers = hasDesktopAUsers.Count;
Out of topic:
It is recommended to have a proper naming convention for handling single and plural terms to avoid confusion. For example:
public class User
{
public string UserName { get; set; }
public List<UserDesktop> UserDesktops { get; set; }
}
public class UserDesktop
{
public int ID { get; set; }
public string DesktopName { get; set; }
public string Type { get; set; }
}
From the above, you should name as User as it is an User object while UserDesktops with 's' as it is a collection set.
Final solution:
List<User> hasDesktopAUsers = users.Where(u => u.UserDesktops.Any(ud => ud.Type == "A"))
.ToList();
int numberOfDesktopAUsers = hasDesktopAUsers.Count;

Get Table A record if Table B has a match in a search

I have two tables
CREATE TABLE RetailGroup(
Id int IDENTITY(1,1),
GroupName nvarchar(50),
)
CREATE TABLE RetailStore(
Id int IDENTITY(1,1),
StoreName nvarchar(100),
RetailGroupId int
)
Where RetailGroupId in RetailStore is referencing RetailGroup ID. I am trying to create a search function where I can search for both RetailGroup and RetailsStores. If I get a matching RetailStore I want to return the Group it is tied to and the matching Store record. If I get a matching Group, I want the group record but no retail stores.
I tried to do it the following way:
public List<RetailGroup> SearchGroupsAndStores(string value)
{
return _context.RetailGroups.Where(group => group.GroupName.Contains(value)).Include(group => group.RetailStores.Where(store => store.StoreName.Contains(value))).ToList();
}
But this is wrong because include should not be used for selection.
Here is my entity framework model for groups
public class RetailGroup
{
[Key]
public int Id { set; get; }
[MaxLength(50)]
public String GroupName { set; get; }
//Relations
public ICollection<RetailStore> RetailStores { set; get; }
}
And here is the one for the store
public class RetailStore
{
[Key]
public int Id { get; set; }
[MaxLength(100)]
public string StoreName { get; set; }
[ForeignKey("RetailGroup")]
public int RetailGroupId { get; set; }
//Relations
public RetailGroup RetailGroup { get; set; }
public ICollection<EGPLicense> Licenses { get; set; }
}
How do I create my LINQ to get the results I am looking for ?
The query returning the desired result with projection is not hard:
var dbQuery = _context.RetailGroups
.Select(rg => new
{
Group = rg,
Stores = rg.RetailStores.Where(rs => rs.StoreName.Contains(value))
})
.Where(r => r.Group.GroupName.Contains(value) || r.Stores.Any());
The problem is that you want the result to be contained in the entity class, and EF6 neither supports projecting to entity types nor filtered includes.
To overcome these limitations, you can switch to LINQ to Objects context by using AsEnumerable() method, which at that point will effectively execute the database query, and then use delegate block to extract the entity instance from the anonymous type projection, bind the filtered collection to it and return it:
var result = dbQuery
.AsEnumerable()
.Select(r =>
{
r.Group.RetailStores = r.Stores.ToList();
return r.Group;
})
.ToList();
Try using an OR condition to filter both the group name and the store name.
return _context.RetailGroups
.Where(group => group.GroupName.Contains(value) || group.RetailStores.Any(store => store.StoreName.Contains(value)))
.Include(group => group.RetailStores.Where(store => store.StoreName.Contains(value)))
.ToList();
Another option would be doing 2 searches, one for the groups and other for the stores. The problem here would be geting a unique set of groups from both results.
List<RetailGroup> retailGroups = new List<RetailGroup>();
var groupSearchResults = _context.RetailGroups
.Where(group => group.GroupName.Contains(value))
.Include(group => group.RetailStores.Where(store => store.StoreName.Contains(value)))
.ToList();
var storeSearchResults = _context.RetailStores
.Where(store => store.StoreName.Contains(value))
.Select(store => store.RetailGroup)
.ToList();
retailGroups.AddRange(groupSearchResults);
retailGroups.AddRange(storeSearchResults);
// Remove duplicates by ID
retailGroups = retailGroups
.GroupBy(group => group.Id)
.Select(group => group.First());
Either use OR condition and have the search in one statement :
public List<RetailGroup> SearchGroupsAndStores(string value)
{
return _context.RetailGroups
.Where(rg => rg.GroupName.Contains(value) || rg.RetailStores.Any(rs => rs.StoreName.Contains(value)))
.Include(rg => rg.RetailStores.Where(rs => rs.StoreName.Contains(value)).ToList())
.ToList();
}
Or you can split the search, first look for RetailGroups then search RetailStores and return their RetailGroup :
public List<RetailGroup> SearchGroupsAndStores(string value)
{
List<RetailGroup> searchResults = new List<RetailGroup>();
searchResults.AddRange(_context.RetailGroups.Where(rg => rg.GroupName.Contains(value)).ToList());
searchResults.AddRange(_context.RetailStores.Where(rs => rs.StoreName.Contains(value)).Select(rs => rs.RetailGroup).ToList());
}

How can I do an EF query on a table to show results only when there's no link to a child table?

I have the following two classes:
public class Word
{
public System.Guid WordId { get; set; } // WordId (Primary key)
public string Name { get; set; } // Name (length: 20)
// Reverse navigation
public virtual System.Collections.Generic.ICollection<WordForm> WordForms { get; set; } // WordForm.FK_WordFormWord
}
public class WordForm
{
public System.Guid WordFormId { get; set; } // WordFormId (Primary key)
public System.Guid WordId { get; set; } // WordId
public string Definition { get; set; } // Definition (length: 500)
// Foreign keys
public virtual Word Word { get; set; } // FK_WordFormWord
}
The word table has some words with matching WordForms and some that have none. What I need is to get a list of words that have no corresponding WordForms.
Here's the code I have so far. I know how to add a simple .Where but this need I have is something I've not seen how to do:
var words = db.Words
.AsNoTracking()
.ToList();
Can someone tell me how I can modify this code to meet my requirements?
A Sub Query should work:
var words = db.Words
.Where(w=> !db.WordForms.Any(z=> z.WordId == w.WordId))
.AsNoTracking()
.ToList();
You can easily get the Words which has no WordForms like this :
var words = db.Words
.Where(W => W.WordForms.Count == 0)
.ToList();
this code works and not need to filter in any statement!
var words = db.Words.Where(e => !e.WordForms.Any())
.AsNoTracking()
.ToList();
Another option is to use a left join
var words = (from w in db.Words
join wf in db.WordForms
on w.WordId equals wf.WordId into wwf
from x in wwf.DefaultIfEmpty()
where x == null
select w)
.AsNoTracking()
.ToList();

C# - All values where ID is 1 from List

I got a List that contains all the employees, now I need to dig in to a specific employee on a new page. I want to get all the values from the employee where the ID is 1 for example. Is there a sollution for this in LINQ?
It's practically a Query SELECT * FROM Employee WHERE id = 1;
class Employee
{
public int EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Title { get; set; }
public string TitleOfCourtesy { get; set; }
public DateTime BirthDate { get; set; }
public DateTime HireDate { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public string HomePhone { get; set; }
public string Extension { get; set; }
//public Image Photo { get; set; }
public string Notes { get; set; }
public int ReportsTo { get; set; }
public string PhotoPath { get; set; }
}
I tried it like this but it doesn't work:
List<Employee> employees = Database.getEmployees();
var uniqUsers = employees.Where(x => employees.Contains(x.EmployeeID == 1)).ToList();
Where employee is type of IEnumerable<Employee>
If you are expecting 1 record:
var result = employee.FirstOrDefault(x => x.EmployeeID == 1); // Returns Employee
If you are expecting more than 1 record:
var result = employee.Where(x => x.EmployeeID == 1); // Return IEnumerable<Employee>
Please note, when using FirstOrDefault if there is no items in your collection (or doesn't match your lambda) then it will return default T which in your case will be Employee and it will be null.
If you want a "single" item that meets that critera use the Single Linq statement:
Employee employee = employees.Single(e => e.EmployeeID == 1);
or
Employee employee = employees.SingleOrDefault(e => e.EmployeeID == 1);
if you want the query to return null instead of throwing an exception if there is not an item in the list that meets that criteria.
Let EmployeeList is the current List of Employees. You can use LINQ to filter the required details as like the specified query by using this(IT will give you all sublist satisfies the specified condition):
int empIdToSearch=1;
List<Employee> FilteredList=EmployeeList.Where(x=>x.EmployeeID ==empIdToSearch).ToList();
If the EmployeeID is unique then there will be one item in the list with particular ID, You can use FirstOrDefault to get the First item from the collection that satisfies the condition.ie.,
Employee EmployeeObject= FilteredList.FirstOrDefault(x => x.EmployeeID == empIdToSearch);
The concept that you need to get is how most linq queries operate.
When you say .Where(x => x.EmployeeID == 1) then x is a single empolyee as if you said:
foreach(Employee x in employees)
{
if(x.EmployeeID == 1)
// take it
}
So the correct syntax would be:
List<Employee> uniqUsers = employees.Where(x => x.EmployeeID == 1).ToList();
Single Optional Result:
Employee uniqUser = employees.SingleOrDefault(x => x.EmployeeID == 1);
Single Mandatory Result:
Employee uniqUser = employees.Single(x => x.EmployeeID == 1);
First Optional Result:
Employee uniqUser = employees.FirstOrDefault(x => x.EmployeeID == 1);
First Mandatory Result:
Employee uniqUser = employees.First(x => x.EmployeeID == 1);
We can fetch the records from collection in two ways.
Linq to sql like query
var employee= from emp in employees where emp.ID==1;
Linq to extension methods.
var employee = employees.Where(emp=>emp.ID==1);
Linq supports a query syntax that is closer to SQL.
var employee1 = from employee in employees where employee.EmployeeID == 1 select employee;
foreach (var x in employee1)
{
Console.WriteLine(x.EmployeeID);
}
The compiler converts all query syntax to method syntax. Not all things can be done with query syntax. The 'from' comes before the 'select' so auto-complete is more useful. It is important to note the linq query is not executed until it is used. The foreach loop is where it is first used in this example.

Convert IGrouping from one class to another using linq

I have two classes Teams and PlayerTeams
public class PlayerTeams
{
public int ID { get; set; }
public string PlayerName { get; set; }
public string PlayerCountry { get; set; }
public bool IsActive { get; set; }
public string PlayerTeam { get; set; }
}
public class Players
{
public int ID { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public bool? Status { get; set; }
}
I have a list of PlayerTeams which is grouped by PlayerTeam like this.
var groupedPlayers = teams
.OrderBy(x => x.PlayerName)
.GroupBy( x => x.PlayerTeam)
.ToList();
Its of type List<IGrouping<string, PlayerTeams>> but I want it to be of type List<IGrouping<string, Players>> as I do not want the redundant key information on every row.
How could I possibly achieve that? I could only think of something like .ConvertAll() on the IGrouping. I am not able to make it also.
Is there an efiicient way to do this?
If you can change the grouping, I'd just use:
var groupedPlayers = teams
.OrderBy(x => x.PlayerName)
.GroupBy(x => x.PlayerTeam, Players.FromPlayerTeam)
.ToList();
Where Players.FromPlayerTeam is a static method in Players which takes a PlayerTeam and returns a Players.
Additionally, I'd suggest using ToLookup instead of GroupBy - a Lookup is exactly what you want here, without bothering with the ToList call.
This not testet, just an idea.
If you have trouble converting your linq statement, which is expecting the IGrouping type, to a string list, then you might have to select it before.
var groupedPlayers = new List<string>();
groupedPlayers = teams
.OrderBy(x => x.PlayerName)
.GroupBy(x => x.PlayerTeam, Players.FromPlayerTeam)
.Select(x => x.Key) // << added select
.ToList();

Categories

Resources