C# String Search with multiple substrings












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;
}









share|improve this question




















  • 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.

    – code4life
    Nov 16 '18 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?

    – Xeyth
    Nov 16 '18 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.

    – Orel Eraki
    Nov 16 '18 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.

    – code4life
    Nov 16 '18 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.

    – Orel Eraki
    Nov 16 '18 at 19:05


















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;
}









share|improve this question




















  • 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.

    – code4life
    Nov 16 '18 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?

    – Xeyth
    Nov 16 '18 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.

    – Orel Eraki
    Nov 16 '18 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.

    – code4life
    Nov 16 '18 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.

    – Orel Eraki
    Nov 16 '18 at 19:05
















1












1








1


2






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;
}









share|improve this question
















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;
}






c# string search






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 20 '18 at 9:12







Xeyth

















asked Nov 16 '18 at 18:41









XeythXeyth

606




606








  • 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.

    – code4life
    Nov 16 '18 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?

    – Xeyth
    Nov 16 '18 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.

    – Orel Eraki
    Nov 16 '18 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.

    – code4life
    Nov 16 '18 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.

    – Orel Eraki
    Nov 16 '18 at 19:05
















  • 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.

    – code4life
    Nov 16 '18 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?

    – Xeyth
    Nov 16 '18 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.

    – Orel Eraki
    Nov 16 '18 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.

    – code4life
    Nov 16 '18 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.

    – Orel Eraki
    Nov 16 '18 at 19:05










3




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.

– code4life
Nov 16 '18 at 18:49







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.

– code4life
Nov 16 '18 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?

– Xeyth
Nov 16 '18 at 18:56





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?

– Xeyth
Nov 16 '18 at 18:56




1




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.

– Orel Eraki
Nov 16 '18 at 18:57





@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.

– Orel Eraki
Nov 16 '18 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.

– code4life
Nov 16 '18 at 18:59





@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.

– code4life
Nov 16 '18 at 18:59




1




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.

– Orel Eraki
Nov 16 '18 at 19:05







@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.

– Orel Eraki
Nov 16 '18 at 19:05














1 Answer
1






active

oldest

votes


















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();





share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53343639%2fc-sharp-string-search-with-multiple-substrings%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    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();





    share|improve this answer




























      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();





      share|improve this answer


























        2












        2








        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();





        share|improve this answer













        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();






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 16 '18 at 20:32









        NetMageNetMage

        13.3k11935




        13.3k11935






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53343639%2fc-sharp-string-search-with-multiple-substrings%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            MongoDB - Not Authorized To Execute Command

            How to fix TextFormField cause rebuild widget in Flutter

            in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith