Linq Select All Items Matching Array - c#

I have a data collection of type IEnumerable<Objects.LabourHours> containing labour records for various employees. I wish to filter the list and return only records for selected employees, which is specified by a list of int[] employees containing the EmployeeIDs.
class LabourHours
{
public int ID {get;set;}
public int EmployeeID {get;set;}
public int HoursWorked {get;set;}
}
How would I go about this? I am sure this has been asked before but I can't find anything similar on here. The closest I have found involves grouping the records by UserID, which is not what I need - I need the actual records.

You can filter your list with LINQ Where using Contains method:
var result = list.Where(x => employees.Contains(x.EmployeeID));

If you want the result to preserve the order of the employees array, you can use Select on the array. From the doc, it "Projects each element of a sequence into a new form", which is basically what you'd want to do in this case.
var result = employees.Select(e => labourHours.First(l => l.EmployeeID == e));
Use FirstOrDefault if all employees don't necessarily have an associated labourHours entry.

Related

How to query a list and return values where ID = any value in an array

I have 2 collections. 1 is an array of groupIds, and the other is an IEnumerable.
My user class contains another list of type IList, which denotes the groups the user belongs to:
public class User
{
public IList<Group> Groups { get; protected set; }
}
And the Group class looks something like this:
public class Group{
public long Group_Id { get; protected set; }
}
What I need to do is return a collection of all users who are in the groupIds array. I.e., I would want something like:
usersRepository.Where(user => user.Groups.Where(group => groupIds.Contains(group.id))
Such that if my groupIds array has group id of 5 and 6, all users in the useresRepository which belong to group 5 and 6 are returned.
So far, the best I've been able to come up with is the following:
UserRepo.Users.AsEnumerable().Where(user =>
user.GroupUsers.Where(wg => GroupIds.Contains(wg.Group_Id)));
But this is complaining since .Contains returns a boolean instead of my list, but I'm not sure how to fix that.
My code doesn't work due to .Contains() causing the expression to return a boolean rather than a collection. However, I'm not sure how else to check of the list of GroupIds nested within my User object contains the specified group ids.
Try Any .This will give you all the users that have any of the group mentioned in the groupIds array
UserRepo.Users.AsEnumerable().Where(user =>
user.Groups.Any(wg => GroupIds.Contains(wg.Group_Id)));
So you have a sequence of Users, where every User has a sequence of Groups, where every Group has a GroupId.
You also have a sequence of GroupIds.
IEnumerable <int> groupIds =
IQueryable<User> users = ...
You want all Users, that have at least one Group with a GroupId that is in you sequence of GroupIds.
Whenever you see "I want all items that have at least one ...", consider using Any:
var result = users
.Where(user => user.groups
.Select(group => group.groupId)
.Any(groupId => groupIds.Contains(groupId));
In words: from every user, take its collection of Groups. From every Group take the groupIds, and check if any of these groupIds is in collection groupIds. If so, keep this user in your result.
This can be a little bit shorter:
var result = users.Where(user => user.groups.Any(group => groupIds.Contains(group.groupId));

Finding similar properties within a list of objects

I have a class Employee:
public class Employee
{
public string SSN;
public string empNumber;
public int someValue;
}
I want to check if the employees share a similar SSN AND a similar empNumber. I have a List<Employee> available to search through. Employees cannot have the same SSN and empNumber. Ultimately, I want to populate a list that contains the employees that share only SSN and empNumber. If this list is not a size 0, then I know to send an error message.
I know I can use LINQ or a foreach, but I am not sure which would be best for this situation.
Seems like a pretty simple GroupBy - assuming your List<Employee> is in a variable employees:
var dupes = employees.GroupBy(e => new {e.SSN, e.empNumber})
.Where(g => g.Count() > 1);
The variable dupes will now contain an enumerable list of anonymous objects with the properties
SSN
empNumber
which will represent your duplicates. Each item is also itself an IEnumerable<Customer> containing the duplicates themselves (from the original list).

Distinct rows from db table with unique IDs but same names

I am creating a videorental system and I'm told that there are to be multiple entries of the same movie, as in a real videostore, but they should all have unique IDs. I'm using LINQ to fetch the data, and currently my view shows me the entire table, so the same movie name gets repeated alot. I tried using the .Distinct extension method, but since the IDs are not the same, they are all distinct. I need to ignore the ID portion of the object, but can't figure it out.
public static List<Movie> GetAllMovies()
{
return Context.Movie.ToList();
}
public static List<Movie> GetAllMoviesDistinct()
{
return Context.Movie.Distinct().ToList();
}
These two methods do the exact same thing
You can do this with MoreLINQ:
var result = Context.Movie
.DistinctBy(m => new {
// take all properties except your ID
})
.ToList();
You can use GroupBy to get movies with for example unique name.
return Context.Movie.GroupBy(m => m.Name).Select(x => x.First()).ToList();
You'd need something like this:
public static List<string> GetAllMovieTitlesDistinct()
{
return Context.Movie.GroupBy(x => x.Title).Select(x => x.Key).ToList();
}
Since you have multiple entries for the same movie, it doesn't really make sense to return a random (or just the first) movie with a specific name. What I'd rather do, is get the unique movie titles, select one title and then list all the entries for that.
What the method above does is, it groups all the movies by their names and then just selects each unique movie title. Please note that it does not return a specific entry for each title, as that would be highly arbitrary and might lead to unwanted results.
var groupesList = Context.Movie.GroupBy(x => x.Name,
(key, val) => new {Key = key, Value = val}).ToList();
then you can call Key(unuque) or Value by key for all inform for example all ID

How to filter a list based on another list using Linq?

I have these two lists, one a list of Venue Objects, one a list of BlockedVenues objects.
I need to filter each item in the listOfAllVenues so that it doesn't contain any venue
that is blocked
IQueryable<Venue> listOfAllVenues = MyDB.Venues;
IQueryable<BlockedVenue> listOfBlockedVenues = Mydb.BlockedVenue;
//I need something to accomplish this please
// var listOfAllVenues_WithoutBlocked_Venues =
( Select All venues from listOfAllVenues
where listOfAllVenues.ID is NOT in
listOfBlockedVenues.VenueID)
Please note that yes both list types are different, but listOfAllVenues has an int ID field, and listOfBlockedVenues has a VenueID int field, i need to use these two
Many Thanks
Try this:
var filtered = listOfAllVenues
.Where(x=>!listOfBlockedVenues.Any(y=>y.VenueId == x.Id));
It will get all Venues where Id is not in blockedVenues list
As of NET 6, you can use ExceptBy.
var filtered = allVenues.ExceptBy(blockedVenues.Select(x => x.VenueID), venue => venue.ID);
This will get all venues except those whose ID is in blockedVenues.Select(x => x.VenueID)
https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.exceptby?view=net-6.0

Access field from generic List in GetRange() method?

Does anybody know how to access a specific field from List<>? I cant figure out howto access a specific field in newList object.
List<Liner> LX = new List<Liner>();
public class Liner
{
public double Temperature { get; set; }
public double Moisture { get; set; }
}
newList = LX.OrderBy(x => x.Temperature).ToList();
var lstMXLast = newList.GetRange(8755, 5); // I need only 5 specific Moisture records in this case.
GetRange returns a copy of the list with the given range. So your list needs at least 8760 items. To select only the Moisture property of your objects, you can use LINQ's Select:
var lstMoistures = newList.GetRange(8755, 5).Select(l => l.Moisture).ToList();
Note: you need the ToList at the end only if you want to persist the query. Your ToList at the end of the OrderBy query is useless because you want to chain another query. I would materialze LINQ queries only as late as possible.
You could also use LINQ for the whole thing:
var lstMoistures = newList.Skip(8755).Take(5).Select(l => l.Moisture).ToList();
Assuming that you originally wanted to select the 5 liners with the highest temperature, this should give you the correct result:
var lstMoistures = LX.OrderByDescending(x => x.Temperature).Take(5).Select(l => l.Moisture).ToList();
You can use newList.GetRange(8755, 5).Select(l => l.Moisture) to just get the Moisture component from the five selected Liner records.
Use
var results = newList.GetRange(8755, 5).Select(m => m.Moisture);
It will give you moisture levels of Liner returned by GetRange() i.e. 5 moisture levels.

Categories