简体   繁体   中英

Faster way to filter a list of objects in to another list based on a condition

I have a list ( ios contacts ) which i need to filter based on the firstname , last name and email starting with the match string. I have about 5000 contacts and currently it takes about 2 seconds to filter results. Here is my code.

    var personList = people.FindAll (p => 
                        (p.LastName != null && p.LastName.IndexOf (findstr, StringComparison.OrdinalIgnoreCase) == 0) || (p.FirstName != null && p.FirstName.IndexOf (findstr, StringComparison.OrdinalIgnoreCase) == 0
                            || (p.GetEmails ().ToList ().Count > 0 && (p.GetEmails ().ToList ().FindAll (e => e.Value.IndexOf (findstr, StringComparison.OrdinalIgnoreCase) == 0).Count > 0)))

                    );

Can anyone suggest a faster way to do this? Thanks

Instead of FindAll , and calls to ToList and then Count , use:

var personList = people.Where(p => (p.LastName != null 
    &&  p.LastName.IndexOf(findstr, StringComparison.OrdinalIgnoreCase) == 0)
    || (
        p.FirstName != null && p.FirstName.IndexOf(findstr, StringComparison.OrdinalIgnoreCase) == 0
        || (p.GetEmails().Count > 0 && (p.GetEmails()
                                            .Where(e => 
                                                e.Value.IndexOf(findstr, StringComparison.OrdinalIgnoreCase) == 0).Count > 0))
             ));

Using IndexOf(..) == 0 to compare is the same as asking if that string (Name, LastName or Email) starts with the given findstr . If instead you want to know if the string contains de findstr use the String.Contains or IndexOf(..) != -1

I would also separate those predicates for code clarity like this:

Func<string, bool> filter = s => !String.IsNullOrWhiteSpace(s) && s.StartsWith(findstr, StringComparison.OrdinalIgnoreCase);

var personList = people.Where(p => filter(p.FistName) || filter(p.LastName) || p.GetEmails().Select(e => e.Value).Any(filter));

Now if you want you can do that in parallel:

var personList = people.AsParallel(). Where(p => filter(p.FistName) || filter(p.LastName) || p.GetEmails().Select(e => e.Value).Any(filter));

You can use:

  • Any() instead of ToList().Count > 0
  • First() and then check if it matches the condition instead of IndexOf (findstr, StringComparison.OrdinalIgnoreCase) == 0

You can find full list of queues here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM