7

So I have a set of pairs<string ,string>

And I want to use find() to search for a single string which would be in the "first" of the pair, then if I find that string in first I want to return second from that function.

My current attempt is..

myList::iterator i;

i = theList.find(make_pair(realName, "*"));

return i->second;
Soo Wei Tan
  • 3,262
  • 2
  • 34
  • 36
user1288735
  • 71
  • 1
  • 1
  • 2
  • 7
    Why are you not using a `map`? Other notes: if it is a `std::set`, why is it called `myList`? Have you created a comparison function for `std::pair`s? How does it look like? – David Rodríguez - dribeas Mar 23 '12 at 16:56

3 Answers3

6

Is C++11 acceptable?

auto it = find_if(theList.begin(), theList.end(),
    [&](const pair<string, string>& val) -> bool {
        return val.first == realName;
    });

return it->second;

Or in C++03, first define a functor:

struct MatchFirst
{
        MatchFirst(const string& realName) : realName(realName) {}

        bool operator()(const pair<string, string>& val) {
                return val.first == realName;
        }

        const string& realName;
};

then call it like so:

myList::iterator it = find_if(a.begin(), a.end(), MatchFirst(realName));
return it->second;

This will only return the first match, but from your question, it looks like that's all you're expecting.

Rick Yorgason
  • 1,616
  • 14
  • 22
2

You can use std::set<std::pair<std::string, std::string> > for this but you will need a custom comparison object for this because the pair's relational operator takes both elements for this. That said, it seems as if you actually should use a std::map<std::string, std::string> instead.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • 1
    I disagree, the semantics would be quite different, notably the inability to store several items with the same "key". A `multimap` could be closer in terms of semantics, but then would not guarantee the uniqueness of the pair which might also be desirable. Without knowing the requirement, your answer really is a shot in the dark... – Matthieu M. Mar 23 '12 at 20:20
1

The definition of < for std::pair implements a lexicographical order and "" is the minimum element for strings. Combining this we get:

 typedef std::pair<std::string, std::string> StringPair;
 typedef std::set<StringPair> Set;

 std::string const* find_first(Set const& s, std::string const& key) {
   Set::const_iterator const it = s.lower_bound(std::make_pair(key, ""));

   // Check that it actually points to a valid element whose key is of interest.
   if (it == s.end() or it->first != key) { return 0; }

   // Yata!
   return &it->second;
 }

The trick is using lower_bound appropriately.

Returns an iterator pointing to the first element which does not compare less than value.

  • If it returns end(), then it did not find anything interesting.
  • Otherwise, it->first >= key so we get rid of the > case (of no interest to us)

I would point out though that this only returns the first element of the range. If you are interested in all elements, try:

typedef std::pair<Set::const_iterator, Set::const_iterator> SetItPair;

SetItPair equal_range_first(Set const& s, std::string const& key) {
  StringPair const p = std::make_pair(key, "");
  return std::make_pair(s.lower_bound(p), s.upper_bound(p));
}

This will return the full range of nodes in s whose first element is equal to key. You then just have to iterate over this range:

for (Set::const_iterator it = range.first; it != range.second; ++it) {
  // do something
}

And you don't even have to worry whether the return of lower_bound or upper_bound was end or not.

  • if lower_bound returns end(), then so does upper_bound, and the loop is skipped
  • if lower_bound points to a node for which it->first > key, then upper_bound will point to that same node, and the loop is skipped

That is the power of ranges: no need to make special checks, the ranges just end up empty when there is no match, and so the loop over them... is skipped in a single check.

duedl0r
  • 9,289
  • 3
  • 30
  • 45
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722