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)

Threaded View

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

    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 08: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.

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