Removing items from a dictionary (qualified by a predicate)
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 3 of 3

Thread: Removing items from a dictionary (qualified by a predicate)

Hybrid View

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

    Lightbulb Removing items from a dictionary (qualified by a predicate)

    Although it sounds similar and the topics are in fact related, it's not really about the predicate here. This already has been cleared in http://www.codeguru.com/forum/showthread.php?t=512194.

    In one of my apps I need to remove some items, selected by a predicate, from a sorted dictionary. Here's a model of how I did that:

    Code:
    // Test5.cpp: Hauptprojektdatei.
    
    #include "stdafx.h"
    
    using namespace System;
    using namespace System::Collections::Generic;
    using namespace System::Linq;
    
    // Predicate function to select all keys which are multiples of 3
    
    bool IsMult3(KeyValuePair<int, String ^> kvp)
    {
      return ! (kvp.Key &#37; 3);
    }
    
    int main(array<System::String ^> ^args)
    {
      SortedDictionary<int, String ^> ^dictOrig = gcnew SortedDictionary<int, String ^>;
    
      // Populate original dictionary
    
      for (int i = 1; i <= 10; ++i)
        dictOrig[i] = gcnew String(L'a' + i - 1, 5);
    
      // Select items to be removed into temporary collection
    
      IEnumerable<KeyValuePair<int, String ^>> ^ieToRemove =
        Enumerable::Where(dictOrig, gcnew Func<KeyValuePair<int, String ^>, bool>(IsMult3));
    
      // Construct a new output dictionary as a copy of the original one
    
      SortedDictionary<int, String ^> ^dictNew = gcnew SortedDictionary<int, String ^>(dictOrig);
    
      // Remove selected items from the output dictionary
    
      for each (KeyValuePair<int, String ^> kvp in ieToRemove)
        dictNew->Remove(kvp.Key);
    
      // In my real-life app I would now assign the filtered dictionary's handle to the
      // original handle variable (a class member)
    
      // Output the filtered dictionary
    
      Console::WriteLine("Key\tValue");
      for each (KeyValuePair<int, String ^> kvp in dictNew)
        Console::WriteLine("{0}\t{1}", kvp.Key, kvp.Value);
    
      Console::WriteLine();
      Console::WriteLine("Hit enter key to continue...");
      Console::ReadLine();
      return 0;
    }
    Ok, this does what I want without any problems. However, inspired by a current thread about native C++ where some really prominent forum members advocated for always favoring STL algorithms over loops (to which I agree of course and also of course that's not really a new point), I tried to achieve the same without a loop.

    First thing I did was modelling the same thing in native C++:

    Code:
    // Test5a.cpp
    
    #include <iostream>
    #include <map>
    #include <vector>
    #include <string>
    #include <algorithm>
    #include <iterator>
    
    using namespace std;
    
    // Predicate function to select all keys which are multiples of 3
    
    bool IsMult3(pair<int, string> kvp)
    {
      return ! (kvp.first % 3);
    }
    
    int main()
    {
      map<int, string> mapOrig;
    
      // Populate original map
    
      for (int i = 1; i <= 10; ++i)
        mapOrig[i] = string(5, 'a' + i - 1);
    
      // Construct an empty output vector to hold the items that passed the filter (i.e. did NOT match the predicate)
    
      vector<pair<int, string>> vctNew;
    
      // Copy items while removing those matching the predicate
    
      remove_copy_if(mapOrig.begin(), mapOrig.end(), back_inserter(vctNew), IsMult3);
    
      // Construct a new output map from the temporary vector
    
      map<int, string> mapNew(vctNew.begin(), vctNew.end());
    
      // Output the filtered map
    
      cout << "Key\tValue" << endl;
      for_each(mapNew.begin(), mapNew.end(),
        [](pair<int, string> kvp) { cout << kvp.first << '\t' << kvp.second << endl; });
    
      return 0;
    }
    It surprised me a bit that this wasn't that easy either because it turned out that remove_if() can't be used for the std::map and I can't create a back insterter for it either. Hence the need for the temporary vector. However, the only "real" loop now is the one that populates the original map and this is not part of the filtering process, so: Mission accomplished!

    (Note that in my original app I need the collection of items that have been removed for logging. I have left that out of the C++/CLI sample above for brevity but the collection is there. However, this is not accounted for in the native sample.)

    And this is as close as I could get as of now in C++/CLI:

    Code:
    // Test5b.cpp: Hauptprojektdatei.
    
    #include "stdafx.h"
    
    using namespace System;
    using namespace System::Collections::Generic;
    using namespace System::Linq;
    
    // Predicate function to select all keys which are multiples of 3
    
    bool IsMult3(KeyValuePair<int, String ^> kvp)
    {
      return ! (kvp.Key % 3);
    }
    
    // Key/element selector functions for Enumerable::ToDictionary()
    
    int KeySelector(KeyValuePair<int, String ^> kvp)
    {
      return kvp.Key;
    }
    
    String ^ValueSelector(KeyValuePair<int, String ^> kvp)
    {
      return kvp.Value;
    }
    
    int main(array<System::String ^> ^args)
    {
      SortedDictionary<int, String ^> ^dictOrig = gcnew SortedDictionary<int, String ^>;
    
      // Populate original dictionary
    
      for (int i = 1; i <= 10; ++i)
        dictOrig[i] = gcnew String(L'a' + i - 1, 5);
    
      // Select items to be removed into temporary collection
    
      IEnumerable<KeyValuePair<int, String ^>> ^ieToRemove =
        Enumerable::Where(dictOrig, gcnew Func<KeyValuePair<int, String ^>, bool>(IsMult3));
    
      // Generate an IEnumerable containing only the items that passed the filter
    
      IEnumerable<KeyValuePair<int, String ^>> ^ieFiltered = Enumerable::Except(dictOrig, ieToRemove);
    
      // Construct a new output dictionary from the filtered items
    
      SortedDictionary<int, String ^> ^dictNew =
        gcnew SortedDictionary<int, String ^>(Enumerable::ToDictionary(ieFiltered,
        gcnew Func<KeyValuePair<int, String ^>, int>(KeySelector),
        gcnew Func<KeyValuePair<int, String ^>, String ^>(ValueSelector)));
    
      // In my real-life app I would now assign the filtered dictionary's handle to the
      // original handle variable (a class member)
    
      // Output the filtered dictionary
    
      Console::WriteLine("Key\tValue");
      for each (KeyValuePair<int, String ^> kvp in dictNew)
        Console::WriteLine("{0}\t{1}", kvp.Key, kvp.Value);
    
      Console::WriteLine();
      Console::WriteLine("Hit enter key to continue...");
      Console::ReadLine();
      return 0;
    }
    Well, it is loopless but I find it a bit bulky (66 lines vs. 52 lines in the C++/CLI sample with the loop). Did I miss anything elegant/straightforward?

    Comments welcome.

    Yes, Alex F, I know your opinion about C++/CLI, like, e.g., in http://www.codeguru.com/forum/showthread.php?p=2014700. And there actually are lots of C# samples of things like that that are pretty short and simple, while samples in C++/CLI apparently are lacking completely in that region of MSDN. However... I think you know my opinion about C++/CLI as well... EDIT: Alex F, of course I'm not meaning by that that your comments aren't welcome.

    BTW, is there any essential difference between Predicate<T> and Func<T, bool>? I noticed that they're apparently not interchangable but is this just because they're not related closely enough in the type derivation tree or is there any secret reason to that I just didn't discover yet?
    Last edited by Eri523; May 23rd, 2011 at 09:01 PM. Reason: Clarification (plus some minor fixes)
    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,102

    Re: Removing items from a dictionary (qualified by a predicate)

    Quote Originally Posted by Eri523 View Post
    BTW, is there any essential difference between Predicate<T> and Func<T, bool>? I noticed that they're apparently not interchangable but is this just because they're not related closely enough in the type derivation tree or is there any secret reason to the that I didn't discover yet?
    Just a quick post regarding the last few lines: Predicate<T> 's been there since .NET 2.0; I believe that they added Func<T, TResult> when they threw in lambdas and LINQ, to support the design. For example, the latter is used with this extension method. Most likely they were had different overall design goals, working on the different parts of the system - I don't think that there's any deeper reason.
    ...
    Actually, I think there are a few things that are related to the way they decided to implement delegates, that may have something to do with why specifically Func<T, bool> is not interchangeable with Predicate<T> (which returns a bool). I'll try to dig out an article...

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

    Re: Removing items from a dictionary (qualified by a predicate)

    Quote Originally Posted by TheGreatCthulhu View Post
    Just a quick post regarding the last few lines: Predicate<T> 's been there since .NET 2.0; I believe that they added Func<T, TResult> when they threw in lambdas and LINQ, to support the design. For example, the latter is used with this extension method. Most likely they were had different overall design goals, working on the different parts of the system - I don't think that there's any deeper reason.
    Ah! The order in which they were introduced pretty much explains their incompatibility: It would have made sense to derive Predicate<T> from Func<T, TResult> while doing it the other way 'round definitely would not. But since Predicate<T> is older, that would require a fundamental change in the existing type hierarchy and that's probably either simply impossible or they considered it too radical a change.

    Yes, it really looks like you just briefly skimmed my post. I'm using that very extension method you link to in my examples #1 and #3 and writing the corresponding code in the real app was the occasion where I discovered that there is some sort of alternative to Predicate<T> at all.

    I find the inheritance rules regarding delegates somehow obscure anyway. But at least one thing I have learned so far: I myself can't derive anything from any delegate at all anyway. This is reserved for the folks who write the runtime and the compilers.

    [...] I'll try to dig out an article...
    I would really appreciate that but don't take too much effort. I can very well live with the incompatibility between these two delegates, but, you know, I like to know the reasons for things...
    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.

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