Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Search a list of objects for any and all matches

Tags:

c#

.net

linq

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?

like image 297
user948060 Avatar asked Jan 10 '13 20:01

user948060


People also ask

How to get all elements from list in c#?

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.

Can you have a list of objects in C#?

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.

How do you search for something in Python?

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.

Can we convert list to object?

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.


3 Answers

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();
like image 141
casperOne Avatar answered Sep 17 '22 23:09

casperOne


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.

like image 26
Justin Niessner Avatar answered Sep 17 '22 23:09

Justin Niessner


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")
like image 36
Tomas Grosup Avatar answered Sep 18 '22 23:09

Tomas Grosup