Prashant
Prashant

Reputation: 721

search properties in list of object c#

There is a list of persons and search object. Search object is to do search on main list.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DOB { get; set; }
    public int Zip { get; set; }
    public string OtherDetails { get; set; }
}

public class SearchParam
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime? DOB { get; set; }
}

I am using SearchParam object to search on List<Person>. My search function looks like this:

List<Person> GetResult(SearchParam search, List<Person> persons)
{
    if (search == null && persons == null)
    {

        if (!string.IsNullOrWhiteSpace(search.FirstName)
            && string.IsNullOrWhiteSpace(search.LastName)
            && !search.DOB.HasValue
            )
        {
            return persons.Where(p => p.FirstName.ToUpper().StartsWith(search.FirstName.ToUpper())).ToList();
        }

        if (string.IsNullOrWhiteSpace(search.FirstName)
            && !string.IsNullOrWhiteSpace(search.LastName)
            && !search.DOB.HasValue
            )
        {
            return persons.Where(p => p.LastName.ToUpper().StartsWith(search.LastName.ToUpper())).ToList();
        }

        if (string.IsNullOrWhiteSpace(search.FirstName)
            && string.IsNullOrWhiteSpace(search.LastName)
            && search.DOB.HasValue
           )
        {
            return persons.Where(p => p.DOB.Equals(search.DOB)).ToList();
        }

        if (!string.IsNullOrWhiteSpace(search.FirstName)
            && !string.IsNullOrWhiteSpace(search.LastName)
            && !search.DOB.HasValue
           )
        {
            return persons.Where(p => p.FirstName.ToUpper().StartsWith(search.FirstName.ToUpper())
                    && p.LastName.ToUpper().StartsWith(search.LastName.ToUpper())).ToList();
        }

        if (!string.IsNullOrWhiteSpace(search.FirstName)
           && !string.IsNullOrWhiteSpace(search.LastName)
           && search.DOB.HasValue
           )
        {
            return persons.Where(p => p.FirstName.ToUpper().StartsWith(search.FirstName.ToUpper())
                    && p.LastName.ToUpper().StartsWith(search.LastName.ToUpper())
                    && p.DOB.Equals(search.DOB)).ToList();
        }
        return persons;
    }
    else
    {
        return persons;
    }
}

This code "GetResult" is working but I am not satisfied with this it. How should I handle null values in the list and in search object and minimize the lines of code in this methods?

Upvotes: 0

Views: 89

Answers (3)

KozhevnikovDmitry
KozhevnikovDmitry

Reputation: 1720

You can minimize if-else clauses and nested blocks with code like this:

public static List<Person> GetResult(SearchParam search, List<Person> persons)
{
    if (search == null || persons == null)
    {
        return persons;
    }

    var ignoreCase = StringComparison.CurrentCultureIgnoreCase;
    var firstNamePrefix = string.IsNullOrWhiteSpace(search.FirstName) ? "" : search.FirstName;
    var lastNamePrefix = string.IsNullOrWhiteSpace(search.LastName) ? "" : search.LastName;

    return persons.Where(p => p.FirstName.StartsWith(firstNamePrefix, ignoreCase))
                  .Where(p => p.LastName.StartsWith(lastNamePrefix, ignoreCase))
                  .Where(p => p.DOB.Equals(search.DOB ?? p.DOB))
                  .ToList();
}

P.S. I took the liberty to invert your first condition, that checked argument for null.

Upvotes: 0

tmaj
tmaj

Reputation: 34947

I suggest an approach with less returns.

Here's my attempt:

/// <summary>
/// Returns a filtered list of persons
/// </summary>
/// <param name="search">Filters. Only filters that are set (not null or empty) are applied</param>
/// <param name="persons">List to filter</param>
/// <returns>Filtered list or a new list of all persons if no filters provided</returns>
/// <exception cref="ArgumentNullException"> Thrown if 'search' or 'persons' is null </exception>
static List<Person> Filter(SearchParam search, IEnumerable<Person> persons)
{
    if( search == null ) throw new ArgumentNullException(nameof(search));
    if (persons == null) throw new ArgumentNullException(nameof(persons));

    IEnumerable<Person> filtered = persons;

    if( !string.IsNullOrEmpty(search.FirstName))
    {
        filtered = filtered.Where( p => string.Compare( p.FirstName, search.FirstName, StringComparison.CurrentCultureIgnoreCase ) == 0);
    }

    if (!string.IsNullOrEmpty(search.LastName))
    {
        filtered = filtered.Where(p => string.Compare(p.LastName, search.LastName, StringComparison.CurrentCultureIgnoreCase) == 0);
    }

    if (search.DOB.HasValue)
    {
        //This filter should probably allow searching only by year, etc.
        filtered = filtered.Where(p =>p.DOB == search.DOB);
    }

    return filtered.ToList();
}

And the example

List<Person> l = new List<Person>();

l.Add(new Person
{
    FirstName = "Prashant",
    DOB = new DateTime(1990, 01, 02)
});
l.Add(new Person
{
    FirstName = "TymTam",
    LastName = "No Choc",
    DOB = new DateTime(1977, 01, 02)
});
l.Add(new Person
{
    FirstName = "Melissa",
    LastName = "No Choc",
    DOB = new DateTime(1977, 01, 02)
});

var filter = new SearchParam()
{
    LastName = "No CHOC",
    DOB = new DateTime(1977, 01, 02)
};

var filtered = Filter(filter, l);
foreach( var f in filtered)
{
    Console.WriteLine($"{f.FirstName} {f.LastName} ({f.DOB})");
}

Result:

TymTam No Choc (2/01/1977 00:00:00)
Melissa No Choc (2/01/1977 00:00:00)

Don't forget about Unit Tests!

Upvotes: 1

pm100
pm100

Reputation: 50110

Use Linqs pipelining

var workingList = persons;

if(searchParam.FirstName != null)
  workingList = workingList.Where(p=>p.FirstName.Startswith(searchParam.FirstName));

if(searchParam.LastName != null)
  workingList = workingList.Where(p=>p.LAstName.StartsWith(searchParam.LastName));

etc.

Finally

return workingList.ToList();

ie assemble the filters one at a time and then do the enumeration at the end

Upvotes: 2

Related Questions