I have a List<T>
of User
instances, and I need to do a search over all the fields in the User
.
What's the most efficient way of doing this?
This is the definition of my User
class:
public class User
{
public String SamAccountName { get; set; }
public String EmailAddress { get; set; }
public String WorkPhone { get; set; }
public String MobilePhone { get; set; }
public String Office { get; set; }
}
I need to search for to see if the value of "Tom" is contained in any of the strings in any of the fields and return a new List<T>
with only the instances that match that criteria.
I'd like to do this with LINQ but don't know how. How can I do this?
C# | How to get all elements of a List that match the conditions specified by the predicate. List<T>. FindAll(Predicate<T>) Method is used to get all the elements that match the conditions defined by the specified predicate.
List in C# is a collection of strongly typed objects. These objects can be easily accessed using their respective index. Index calling gives the flexibility to sort, search, and modify lists if required. In simple, List in C# is the generic version of the ArrayList.
The find(query) method is built-in to standard python. Just call the method on the string object to search for a string, like so: obj. find(“search”). The find() method searches for a query string and returns the character position if found.
A list can be converted to a set object using Set constructor. The resultant set will eliminate any duplicate entry present in the list and will contains only the unique values.
Assuming you have an IEnumerable<User>
, you could do this:
// Query for "Tom" being contained in any of the fields.
var query =
from user in users
where
(user.SamAccountName != null && user.SamAccountName.Contains("Tom")) ||
(user.EmailAddress != null && user.EmailAddress.Contains("Tom")) ||
(user.WorkPhone != null && user.WorkPhone.Contains("Tom")) ||
(user.MobilePhone != null && user.MobilePhone.Contains("Tom")) ||
(user.Office != null && user.Office.Contains("Tom"))
select user;
Note, the check for null
is important in the event that any of these fields is null, otherwise, when you call the Contains
method on the String
class it will throw a NullReferenceException
as there's no string to make the call to Contains
on.
The where
clause simply maps to the Where
extension method on the Enumerable
class (make sure to have a using System.Linq;
declaration so the extension methods are recognized by the compiler).
If you feel the null check is excessive and/or repetitive, you could whittle down your code like so:
// Generate your predicate.
Func<string, bool> checkContainsTom = s => s != null && s.Contains("Tom");
// Query.
var query =
from user in users
where
checkContainsTom(user.SamAccountName) ||
checkContainsTom(user.EmailAddress) ||
checkContainsTom(user.WorkPhone) ||
checkContainsTom(user.MobilePhone) ||
checkContainsTom(user.Office)
select user;
This is slightly better, as you're encapsulating the redundant logic; if the logic changes, you only have to change it in one place and it will be applied to all the checks. Feel free to substitute the lambda expression with a function if you want.
You can then enumerate through query
with a foreach
if you want to use deferred execution. If you need it in a materialized list (like a List<User>
), then you just call the ToList
extension method on the Enumerable
class, like so:
IList<User> materializedResults = query.ToList();
It isn't pretty, but it should work:
users.Where(u => u.SamAccountName.Contains("Tom") ||
u.EmaildAddress.Contains("Tom") ||
u.WorkPhone.Contains("Tom") ||
u.MobilePhone.Contains("Tom") ||
u.Office.Contains("Tom"));
Although, I have to say, I don't understand why you'd need to search for the string "Tom" inside of the phone numbers.
Copy one generic extension method in some static class:
public static IEnumerable<T> WhereAtLeastOneProperty<T, PropertyType>(this IEnumerable<T> source, Predicate<PropertyType> predicate)
{
var properties = typeof(T).GetProperties().Where(prop => prop.CanRead && prop.PropertyType == typeof(PropertyType)).ToArray();
return source.Where(item => properties.Any(prop => PropertySatisfiesPredicate(predicate, item, prop)));
}
private static bool PropertySatisfiesPredicate<T, PropertyType>(Predicate<PropertyType> predicate, T item, System.Reflection.PropertyInfo prop)
{
try
{
return predicate((PropertyType)prop.GetValue(item));
}
catch
{
return false;
}
}
And then you just call:
var usersList = new List<User>();
var filteredUsersList = usersList.WhereAtLeastOneProperty((string s) => s.Contains("Tom")).ToList();
Or with some other lambda fitting your needs better. e.g.
.WhereAtLeastOneProperty((string s) => s=="Tom")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With