1

So I'm working on a filter for a resource list and one of the filters is the Name property(string).

As a(dumb) example : resource name is "Big,Red/Square Table" and the filter is "Table Red", it should be a valid resource

This is what I could come up with using the little time I had:

static void ApplyNameFilter(ref ApplicationViewModel model, string filter)
{
    if (string.IsNullOrEmpty(filter) || filter == "") return;
    char[] separators = {' ', ',', '.', '/', '\\', '|', '_', '-'};
    var validResources = new List<ResourceModel>();

    foreach (var resource in model.ResourcesViewModel.Resources)
    {
        var filterSubstrings =
            filter
            .ToLower()
            .Split(separators)
            .ToList();

        var resourceSubstrings =
            resource.Name
            .ToLower()
            .Split(separators)
            .ToList();

        resourceSubstrings.ForEach(substring => {
            if (filterSubstrings.Contains(substring))
                filterSubstrings.RemoveAll(sub => sub == substring);
        });

        if (filterSubstrings.Count == 0)
            validResources.Add(resource);
    }

    model.ResourcesViewModel.Resources = validResources;
}

Should I take a different approach for this?

EDIT: Ended up going with this until I figure out RegEx

static void ApplyNameFilter(ref ApplicationViewModel model, string filter)
{
    if (string.IsNullOrEmpty(filter)) return;

    char[] separators = {' ', ',', '.', '/', '\\', '|', '_', '-'};
    var filterSubstrings =
        filter
            .ToLower()
            .Split(separators)
            .ToList();

    var validResources = model.ResourcesViewModel.Resources
        .Where(resource => filterSubstrings.All(fs => resource.Name.ToLower().Contains(fs)))
        .ToList();

    model.ResourcesViewModel.Resources = validResources;
}    
7
  • 3
    Yeah. Definitely look towards a regex solution - you'll definitely have more concise code. There are other ways to skin this cat, but you'd have to be digging back to your comp sci 101 past and thinking about dynamic programming (at least that's what's coming to my mind, LOL). Single responsibility principle based approach also might make your life a lesser hell, here, as well. In both the regex and non-regex approaches, try to leverage an immutability based approach. You're going to really appreciate that you didn't change things in-place, later on. Commented Nov 16, 2018 at 18:49
  • I haven't used too much RegEx though, I know it's damn powerful. Is there a way to generate proper regex patterns based on ones input? Commented Nov 16, 2018 at 18:56
  • 1
    @Xeyth, To add on code4life excellent comment. I will also clarify, about specific point, if the method you are working with, only need the resource list, you do not want to let the method "know" all of your object and his hierarchy. some of the reasons for that is: in case the hierarchy will change, or you will want to use this logic in some other part of your application. you don't need to pass things that the method doesn't care, nor should you. Commented Nov 16, 2018 at 18:57
  • @Xeyth: absolutely. If you're using .NET 4.5 or higher, it gets even easier since you get to leverage string interpolation. For instance, $"{your variable here}regex expression here" - that's just a very raw, off-the-cuff example. I strongly suggest googling regex, you are not going to need a very complex regex, for what you are trying to do. Commented Nov 16, 2018 at 18:59
  • 1
    @Xeyth, If you only wants to "beautify" your method, you can work with .Intersect or .All, which is immutable and also, almost gives you the solution. e.g: filterSubstrings.Intersect(resourceSubstrings).Count() == filterSubstrings.Count OR filterSubstrings.All(fs => resourceSubstrings.Contains(fs)). Also you don't need to compute the filterSubstring in every iteration, they do not change. so put them out of your loop. Commented Nov 16, 2018 at 19:05

1 Answer 1

2

You can use LINQ to make this more concise (and probably faster, as you are creating Lists and removing elements over every resource unnecessarily, though a non-LINQ solution could be even faster).

var validResources = model.ResourcesViewModel.Resources
                        .Where(resource => {
                            var resourceSubstrings = resource.Name.ToLower().Split(separators).ToHashSet();
                            return filterSubstrings.All(fs => resourceSubstrings.Contains(fs));
                        })
                        .ToList();

If you are willing to accept all the filter substrings being inside the Name regardless of the separators, then you can simplify to just be:

var validResources = model.ResourcesViewModel.Resources
                        .Where(resource => filterSubstrings.All(fs => resource.Name.ToLower().Contains(fs)))
                        .ToList();
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.