开发者

Returning an empty vector of strings if key is not found

开发者 https://www.devze.com 2023-04-05 01:18 出处:网络
I know it is a very bad idea, so other suggestions on how to do it efficiently will be well-received.

I know it is a very bad idea, so other suggestions on how to do it efficiently will be well-received.

Here's the thing. I have map<string,vector<string> > , I want to search for a key and return its corresponding value (vector of strings in this case). Reason I insist on returning (rather than just iterating) is I need to search the values returned in some other vector.

An example will make this clear:

Input:

key1 ---> {2,3,4}
key2 ---> {1}
key3 开发者_运维技巧---> {2,12,11,9}

For key1 as input, vector with values 2,3,4 should be returned. Now these 2,3,4 values need to be searched in other vector of strings. What is the most efficient way to do this?

I tried something like this:

vector<string> returnEdges(string key)
{
    for (map<string, vector<string> >::iterator it=outgoing.begin();
    it!=outgoing.end();++it)
    {
        if (key.compare((*it).first)==0)
        {
            return (*it).second;
        }
    }


    //return string<;//what should I return here????


}

1) How should I return empty vector in case key is not found?

2) what is the best way to implement this?

I hope the question is clear.

EDIT: As I wrote the question, I thought why not return an iterator? Do the people at SO give their approval for this idea?


1) Returning an iterator is a fine idea. When you do this, the natural way to indicate the "not found" case is to return the .end() iterator. This has the down-side that the abstraction is somewhat leaky: the caller has to be able to get at this .end() value in order to compare to it for error checking, and the returned iterator exposes a richer interface than you'd like (the client code shouldn't really play around with incrementing and decrementing the iterator).

2) Returning an empty vector is as simple as creating an empty vector and returning it. Creating an empty vector = constructing a vector which is empty. This is what you get from - drum roll - the default constructor of the vector class.

3) You don't need to, and shouldn't, implement the search loop yourself. The standard library already implements this for you. (There is a specialized find function for map s because of the key/value distinction. For sequences like list, vector and deque, prefer the free function std::find, which comes from <algorithm>.

4) You should prefer to accept function parameters (when they are instances of classes, like std::string) and return data (especially complex things like a vector of strings) by const reference. Passing and returning by value implies a copy; sometimes the compiler can optimize this away, but it's not as reliable as we'd like. Besides, the reason you're using C++ in the first place is to have that level of control over things, right? If not, then don't torture yourself with it.

However, you can't do that if you're going to return a newly-created value some of the time. Yet another way to design the interface is to return a pointer to the vector of strings (note that pointer arithmetic on these will be invalid) within the map, or a NULL pointer if the value is not found. This avoids copying and distinguishes a "not found" result from an actual empty vector within the data, but it means the client code has to deal with an icky raw pointer.

5) Having 'return' in the name of a function is useless, as returning is what functions do. OTOH, it is a good idea to name things in a way that makes it evident why the parameters are what they are.

6) With iterators for complex types, it is often a good idea to set up typedefs.

Returning an iterator is as simple as:

typedef map<string, vector<string> >::iterator graph_iterator;
graph_iterator edges_named(const string& node_name) {
    return outgoing.find(node_name);
}

Returning a vector of strings is as simple as:

typedef map<string, vector<string> >::iterator graph_iterator;
vector<string> edges_named(const string& node_name) {
    graph_iterator it = outgoing.find(node_name);
    return it == outgoing.end() ? vector<string>() : it->second;
}

Returning a pointer is as simple as:

typedef map<string, vector<string> >::iterator graph_iterator;
vector<string>* edges_named(const string& node_name) {
    graph_iterator it = outgoing.find(node_name);
    return it == outgoing.end() ? NULL : &(it->second);
}

Choose wisely.


How about this:

std::vector<std::string> find_by_key_maybe(const std::string & key)
{
  std::map<std::string, std::vector<std::string>>::const_iterator it = themap.find(key);
  return it == themap.end() ? std::vector<std::string>() : it->second;
}

Or even, if themap is non-const, and you also wish to add an empty vector into the map:

std::vector<std::string> find_by_key_and_insert_if_missing(const std::string & key)
{
  return themap[key];
}

It's perfectly OK to return a vector by value, if that's what the situation calls for.


Most of the time you don't need a copy of the vector to be returned, a const reference will do just as well. You can always make a copy later if you need to.

Also you don't need to iterate through a map, they're optimized for searching with the find method.

const vector<string> & returnEdges(string key)
{
    map<string, vector<string> >::iterator it = outgoing.find(key);
    if (it == outgoing.end())
    {
        static vector<string> empty_vector;
        return empty_vector;
    }
    return it->second;
}


I think the operator [] can help you, it will return a empty value(a empty vector here). So all you need to do is

vector<string> returnEdges(string key) 
{
    return outgoing[key];
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号