testing
testing

Reputation: 20279

Filter a list based on multiple criteria and multiple values (unknown number of values)

I have a class User:

public class User{
    // some properties
    public string Name { get; set; }
    public int PerfLoc { get; set; }
    public bool Active { get; set; }
    // constructor would follow ..
}

Than I have a class PerfLoc:

public class PerfLoc{
    // some properties
    public string Number { get; set; }
    // constructor would follow ..
}

I have a list of users List<User> myUsers and a list List<PerfLoc> myPerfLoc. Now I want to find all users of myUsers which have a certain PerfLoc. So I have multiple values in myUsers and multiple values in myPerfLoc. The exercise now is to make a match. I thought about some sort of pseudeo code like this:

List<User> filteredUser = new List<User> ();
foreach(PerfLoc currentPerfLoc in myPerfLoc){
    filteredUser += myUsers.Any(u => u.PerfLoc == currentPerfLoc.Number);
}
filteredUser = filterUser.Any(u => u.Active = true);
filteredUser.Sort();

Here I'd have sorted after PerfLoc and Active (= multiple criteria) and also after multiple values (list of PerfLoc.Number). I saw some from, where, select LINQ expressions and thought that my approach above could be expressed in a shorter way. How would you express that?

Upvotes: 0

Views: 8075

Answers (2)

Rhumborl
Rhumborl

Reputation: 16609

You can do this using a join statement in LINQ. Similar to in SQL you put the two lists together and match them on a property or expression. You can also sort using orderby.

The following is a basic check for Active Users matching a list of PerfLocs:

var filteredUser = from u in myUsers
                join p in myPerfLocs on u.PerfLoc equals p.Number.ToString()
                where u.Active == true
                orderby u // or u.Name if you need to
                select u;

As you are conditionally checking Active state and against myPerfLocs, there may be a way to do it in a single clause but it would look messy, so I would keep it as separate checks anyway - shorter is not always cleaner. You can create a result IEnumerable and then filter that as necessary:

bool checkActive = true;

// set up result list - variable needs to be IEnumerable<User>, not List<User>
var filteredUser = myUsers.AsEnumerable();

// add Active check if necessary
if (checkActive)
{
    filteredUser = filteredUser.Where(u => u.Active);
}

// check against PerfLoc list if necessary
if (myPerfLocs.Any())
{
    filteredUser = from u in filteredUser
                    join p in myPerfLocs on u.PerfLoc equals p.Number.ToString()
                    select u;
}

filteredUser = filteredUser.OrderBy(u => u); // or (u => u.Name) etc if you want

See https://dotnetfiddle.net/hrnNmJ for a working sample.

Upvotes: 1

sloth
sloth

Reputation: 101032

You can use Join. But note that in the code you provided User.PerfLoc is int and PerfLoc.Number is string. If this is really the case then you have to add a conversion to the code below.

var filteredUser = myUsers.Where(u => u.Active)
                          .Join(myPerfLoc, (u) => u.PerfLoc, 
                                           (p) => p.Number, 
                                           (u, p) => u);

Upvotes: 1

Related Questions