2

Say I have a problem statement where my function receives a string (all alphabets) and I need to store the indices of all the alphabets. For example

If input is aabbacd
then I need something like
a -> 0, 1, 4
b -> 2, 3
c -> 5
d -> 6

Now, I wanted to create a map of char to vector, but ended up creating a map of char to vector*.

function(String s)
{
    map<char, vector<int> > m;
    for(i = 0; i < s.size(); i++)
    {
        if(m.find(s[i]) == m.end())
        {
            //create new vector and push 'i'
            vector<int>* v = new vector<int>(); // If I create a vector here, then that will only be live till the closing brace of the if
            .... // rest of the code

Given c++ stl already handles all pointer management, I understand creating a vector* is not the best thing to do. But what can I do in this case, so that the vector created inside the if block remain live outside as well?

Kraken
  • 23,393
  • 37
  • 102
  • 162
  • 3
    Just insert the vector into the map. Or even better, instead of your m.find(), just do a `m[s[i]].push_back(i)` – cplusplusrat Aug 04 '19 at 03:01
  • Please fix your code. `function(String s)` doesn't look like C++. And `i` is not declared. – L. F. Aug 04 '19 at 03:05

3 Answers3

2

The STL containers hold elements by value, i.e., it makes a copy of the vector you want to use. So your worry that the vector goes out of scope is largely futile. Just create a normal vector and move it into the container. Example:

map<char, vector<int>> m;
for (std::size_t i = 0; i < s.size(); ++i) {
    vector<int> v;
    // populate v
    m.emplace(character, std::move(v));
    // now m holds its own *copy* of the vector
}

In fact, it seems you are over-complicating this process. It can be as simple as:

map<char, vector<std::size_t>> m;
for (std::size_t i = 0; i < s.size(); ++i)
    m[s[i]].push_back(i);
return m;

with credits to cplusplusrat.

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • if instead of `m.emplace`, I did a `m.insert(character, v)`, would that cause any problems. If yes, what would those be? I think not, because insert is still going to copy stuff when inserted, and emplace is just to do away with copying process and just steal the copy of vector. Let know if I am off here. thanks – Kraken Aug 04 '19 at 03:24
  • @Kraken Yes, it will cause the program to be ill-formed because `insert` can't be used like this :-) Also, the copying is eliminated by `std::move`. – L. F. Aug 04 '19 at 03:25
  • :), ok if I followed the correct syntax in terms of creating pair of character, vector, how about then? – Kraken Aug 04 '19 at 03:26
  • @Kraken `emplace` is just a wrapper around `insert` that uses perfect forwarding to allow you to simply pass the arguments to the constructor and call the constructor internally. If you use `insert`, you have to call the constructor manually. – L. F. Aug 04 '19 at 03:27
1

The default behavior of std::map is to create a value at that key if none previously existed:

Returns a reference to the object that is associated with a particular key. If the map does not already contain such an object, operator[] inserts the default object data_type().

That is, just by referencing m[myChar], you are creating an empty vector in that place if none existed before. There is no need to create one with new.

Instead, the solution is surprisingly simple:

void function(string s) // note, before you didn't specify a return type That's bad.
{                            // In real C++ code, always specify a return type, or void if none
    // also, I'm assuming you're using an std::string, which is not String even with
    // using namespace std (bad idea to use that, btw)

    map<char, vector<int> > m;
    for(unsigned int i = 0; i < s.size(); i++) // Also, i needs a type. I chose unsigned int
    {
        m[s[i]].push_back(i);
    }
    // do more things...
}

1

but ended up creating a map of char to vector*."

No such thing is in the code. It seems you just ended up creating a memory leak.

But what can I do in this case, so that the vector created inside the if block remain live outside as well?

You don't need to do anything and do not need to keep the vector alive. std::map stores a copy of whatever you put in it. You may build your vector, put it in the map, and forget about it, for the map stores a copy; or you may just build the vector directly in the map:

for(i = 0; i < s.size(); i++)
    m[s[i]].push_back(i);

If you are dealing with English alphabet and ASCII, which seems like the case, you may want to dispense with the map completely and use an array of 26 vectors instead. The code remains almost the same:

for(i = 0; i < s.size(); i++)
    m[s[i] - 'a'].push_back(i);

(assuming the string is lower-cased).

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243