[RESOLVED] Filtering array items according to a dynamically created predicate
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 4 of 4

Thread: [RESOLVED] Filtering array items according to a dynamically created predicate

Hybrid View

  1. #1
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,585

    [RESOLVED] Filtering array items according to a dynamically created predicate

    I just encountered the requirement to filter array items according to a dynamically created predicate, i.e. one determined by user input. I didn't find anything really enlightening on MSDN (the sample on Array::FindAll<T> only demonstrates hard-coded predicates) or CodeGuru, so I rolled my own. It involves an extra class created for the sole purpose of representing the predicate. It goes something like this:

    Code:
    // Test1.cpp: Hauptprojektdatei.
    
    #include "stdafx.h"
    
    using namespace System;
    using namespace System::IO;
    using namespace System::Text::RegularExpressions;
    
    // Regular expression predicate class
    
    public ref class RegexPredicate
    {
    public:
      RegexPredicate(String ^strRegex, RegexOptions opt) : m_regex(gcnew Regex(strRegex, opt))
      {
        m_pred = gcnew Predicate<String ^>(this, &RegexPredicate::PredFunc);
      }
    
      initonly Predicate<String ^> ^m_pred;
    
    private:
      bool PredFunc(String ^s)
      {
        return m_regex->IsMatch(s);
      }
    
      initonly Regex ^m_regex;
    };
    
    int main(array<System::String ^> ^args)
    {
      if (args->Length != 1) {
        Console::WriteLine("Usage: Invoke this program with a dir-style file name match pattern as the only\n"
          "command line argument");
    #ifdef _DEBUG
        Console::WriteLine();
        Console::WriteLine("Hit enter to continue...");
        Console::ReadLine();
    #endif
        return 1;
      }
    
      // Build an array of all file names without absolute path
    
      array<FileInfo ^> ^afi = (gcnew DirectoryInfo("."))->GetFiles();
      array<String ^> ^astrFileNames = gcnew array<String ^>(afi->Length);
      for (int i = 0; i < afi->Length; ++i)
        astrFileNames[i] = afi[i]->Name;
    
      // Construct regex predicate object based on the dir-style wildcard pattern converted to a regex
    
      RegexPredicate ^rxp =
        gcnew RegexPredicate(String::Concat("^", args[0]->Replace(".", "\\.")->Replace("?", ".?")->Replace("*", ".*"), "$"),
        RegexOptions::IgnoreCase);
    
      // Filter the file names according to the predicate
    
      array<String ^> ^astrFilteredFileNames = Array::FindAll(astrFileNames, rxp->m_pred);
    
      // List the filtered file names
    
      for (int i = 0; i < astrFilteredFileNames->Length; ++i)
        Console::WriteLine(astrFilteredFileNames[i]);
      Console::WriteLine();
      Console::WriteLine("{0} out of {1} files matched the predicate", astrFilteredFileNames->Length, astrFileNames->Length);
    
    #ifdef _DEBUG
      Console::WriteLine();
      Console::WriteLine("Hit enter to continue...");
      Console::ReadLine();
    #endif
      return 0;
    }
    You can see, the predicate class holds the raw predicate (i.e. the delegate) and the predicate function, along with the data needed to determine the match. It works like a charm and is nicely encapsulated, however, since this certainly is something that's required in a vast lot of programs, I wonder whether there is any more straightforward and/or elegant way to achieve this.

    Of course the above code is just a demo program. Even my XP already has a dir command and the classes in the System::IO namespace do a good job filtering file names as well. This is just a model of what I need to do (and am currently doing that way) in another app.

    Any comments are welcome.
    Last edited by Eri523; May 7th, 2011 at 11:04 PM. Reason: Fixed a comment in the code
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  2. #2
    Join Date
    Jan 2010
    Posts
    1,099

    Re: Filtering array items according to a dynamically created predicate

    It seems like a nice solution to me. As for a more straightforward way, I first thought that you could maybe use some features of LINQ, but it turns out that the LINQ approach doesn't differ from the Predicate generic delegate - at least from what I could see. (Besides, is LINQ available as an integral feature in C++/CLI anyway? MSDN seems to indicate that it is not; if so - though you can still use it as it is a part of the .NET framework, it would turn out to be a rather cumbersome approach, which means it wouldn't really be more straightforward...)

    Anyway, as I've said - your approach is just fine. But, just for the sake of the argument, I'd like to present an alternate line of thought - it is not better or anything, it's just from a different angle. It involves writing your own version of FindAll function.
    The problem is that the predicate accepts only one parameter - "the object to compare against the criteria defined within the method represented by this delegate", sensu MSDN. But note two things: (1) that parameter can be anything, and (2) we don't have to listen to MSDN in this case.

    So, first, the predicate can have whatever it wants for it's function body, as long as it returns a bool. Then, it would be convenient if you could pass both the object and the regular expression to the the predicate function. And finally, the type of the object passed to it can be anything. You see where I'm going with this?

    Nothing prevents you to model the two parameters with a lightweight class (whose interface would expose both the object to compare and the regex to use), and pass an instance of that "helper" class to the predicate function, which would then extract the data it needs and be able to do it's work.

    Let's call this class PredicateHelper (I can't think of anything better). You have two choices. You can either use the existing FindAll, which would mean that you would have to wrap each of the objects in the collection in an PredicateHelper, and then extract them back (which probably isn't that convenient), or you could write your own FindAll, that would create a PredicateHelper object and reuse it by switching the array object in each iteration. It is some extra work... Besides, whether this approach is more elegant is debatable.
    Again, I like the way you solved it. It does feel strange that Microsoft didn't provide in-framework support for it, though. Or maybe they have, and we just need to do some more digging?

    Edit:
    It occurred to me that what I discussed above is a bit off: I was tired last night and I thought of it on the go, but I guess I didn't think it through - if you create your own version of FindAll, you might as well create your own Predicate delegate without the whole helper class mumbo-jumbo. So, from that perspective - your solution is a really nice way to adapt the existing functionality.
    Last edited by TheGreatCthulhu; May 10th, 2011 at 08:30 AM.

  3. #3
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,585

    Re: Filtering array items according to a dynamically created predicate

    Quote Originally Posted by TheGreatCthulhu View Post
    (Besides, is LINQ available as an integral feature in C++/CLI anyway? MSDN seems to indicate that it is not; if so - though you can still use it as it is a part of the .NET framework, it would turn out to be a rather cumbersome approach, which means it wouldn't really be more straightforward...)
    Taking into account the original meaning of the acronym LINQ, i.e. Language Integrated Query, no, AFAIK this is not supported by C++/CLI. However, I was able to do quite amazing things using the .NET classes in the System::Linq and particularly the System::Linq::Expressions namespaces in C++/CLI. I did only use them in some experiments so far (as opposed to real apps) but I'm looking forward to use them IRL.

    BTW, extension methods aren't supported at the language level in C++/CLI either but once you've found out how they can be used despite that they can be really useful. AFAIK it's not documented in the "core MSDN library" how to do that but I found the trick in a blog entry (well, and that one was on MSDN). I'm too tired to dig out the link at the moment but it can be found using a forum search on this very section here (in one of my own posts ).

    Without being able to use the "Language Integrated" part of LINQ I don't think it really is an option here though.

    Anyway, as I've said - your approach is just fine. But, just for the sake of the argument, I'd like to present an alternate line of thought - it is not better or anything, it's just from a different angle. It involves writing your own version of FindAll function.
    Yes, of course I could do that but that's just the opposite of what I meant by "straightforward". And if I actually did it I would probably not base it on the Array class but instead on the ICollection or IEnumerable interface. The result would probably come pretty close to what's called an extension method by the .NET framework.

    The problem is that the predicate accepts only one parameter - "the object to compare against the criteria defined within the method represented by this delegate", sensu MSDN. But note two things: (1) that parameter can be anything [...].
    The fact that the predicate in the original MS design takes only one parameter is exactly the core problem I was facing here. The two options that looked more or less obvious at first sight were to either create my own extension method as described above or to twist the whole thing to fit by squeezing more information into the object held by the container than I (or the developer who eventually is to use what I created) actually need. Both options didn't look really attractive to me.

    [...] and (2) we don't have to listen to MSDN in this case.


    So, first, the predicate can have whatever it wants for it's function body, as long as it returns a bool. Then, it would be convenient if you could pass both the object and the regular expression to the the predicate function. And finally, the type of the object passed to it can be anything. You see where I'm going with this?
    Yes, I see. But essentially (unless I somehow misunderstand you here) this would turn out to be the "squeezing" option I described above. However, this would open up a really funny side option: Every item in the container could have its very own filtering condition. Just wondering what that might ever be useful for...

    Nothing prevents you to model the two parameters with a lightweight class (whose interface would expose both the object to compare and the regex to use), and pass an instance of that "helper" class to the predicate function, which would then extract the data it needs and be able to do it's work.

    [...]
    Sound like a nice idea (unless I misunderstand it and it's just another "squeezing" variant): I could create a "generic predicate" base class, probably abstract, from which I can derive concrete predicates which don't even necessarily involve regexes. I don't need that for the app in question but it's a nice idea to keep in mind.

    Edit:
    It occurred to me that what I discussed above is a bit off: I was tired last night and I thought of it on the go, but I guess I didn't think it through - if you create your own version of FindAll, you might as well create your own Predicate delegate without the whole helper class mumbo-jumbo. So, from that perspective - your solution is a really nice way to adapt the existing functionality.
    Please note that I didn't just start that thread to hear how nice a code I wrote... The idea to derive my own delegate class actually was something I considered remotely but I dropped it soon due to my limited experience with delegates. Maybe it's something to have a closer look at in the future.

    Due to reasons documented on my profile page I've been considerably strung out the last few days and am pretty tired now as well. Think I'll re-read all the posts in this thread soon, but probably not before reading (almost) all the posts I missed in this and other sections.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  4. #4
    Join Date
    Jul 2002
    Posts
    2,512

    Re: Filtering array items according to a dynamically created predicate

    Quote Originally Posted by Eri523 View Post
    Without being able to use the "Language Integrated" part of LINQ I don't think it really is an option here though.
    Looking at your code, I don't see anything, that cannot be written in C# (well, you already know my opinion about C++/CLI). Anyway, the point is that LINQ is still an option for you (of course, if it is OK for your purposes), assuming that you make all the work in C# class library called from C++/CLI.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center