Search code examples
c++dictionaryboostboost-filesystem

Iterate and compare values in map<string, vector<string>> c++


I have written a program in c++17 using the boost::filesystem library that takes a path as an argument and returns a map:

map<string, vector<string>>

where each key is a directory (boost::filesystem::path converted to string), and each file in each directory is pushed to the value vector.

First, I create a vector of paths from the path given as an argument:

// Method to create vector of paths
vector<path> InToVecsOne(path p, vector<path> v1)
{
  for(auto entry : recursive_directory_iterator(p))
  {
    if(is_directory(entry))
    {
      v1.push_back(entry);
    }
  }
  return v1;
}

Then, I create the map using the vector as follows:

// Function takes a vector of paths and returns map of key-value pair path-vector<string>
map<string,vector<string>> FileMap(vector<path> v1, 
map<string,vector<string>> m, vector<string> v2)
{
  for(auto p : v1)
  {
    // iterate over each entry in path p
    for(auto entry : directory_iterator(p)) 
    {
      if(is_regular_file(entry) == true)
      {
        // add file to vector<string>
        v2.push_back(basename(entry) + " "); 
      }
    }
    // convert path to pathname (DirX) string
    string pathname = basename(p); 
    m.insert(make_pair(pathname, v2));
    v2.erase(v2.begin(), v2.end()); // remove contents after iterating
  }
  return m;
}

Using my sandbox directory as a test path, I get the following output when printing the contents of my map:

DirA: Z X Y 
DirB: Z X Y 
DirBB: X Y YY 
DirC: Z 
DirCC: ZZ X Y YY 

As noticeable, the keys are DirA, DirB, DirBB, etc. and the values are Z, X, Y etc.

What I want to do now is to transform things so that my output looks like:

X : DirA, DirB, DirBB, DirCC
Y : DirA, DirB, DirBB, DirCC

etc.

I think the best way for this would be either to: 1. re-write the map method, or 2. iterate over the contents in the map, compare if the value is associated with a key and add this value to a new data structure.

I am not sure of which would be easier, or how the second one would look like, and hence would need some advice.

Thanks.


Extension:

Following comment to create a map of filename-path pairs, I encountered a new problem after creating this new map from the old map above. My function is as follows:

// Make new map where file is key, and dir is value. m1 is old map, m2 is new map
map<string,vector<string>> FinalMap(map<string,vector<string>> m1, 
map<string,vector<string>> m2, vector<string> dirnames)
{
  // iterate over each key
  for(map<string,vector<string>>::const_iterator it = m1.begin(); it != m1.end(); ++it)
  {
    string dirname = it->first;
    dirnames.push_back(dirname);

    vector<string> files = it->second;
    // iterate over elements in vector<string> files
    for(auto i : files)
    {
      m2.insert(make_pair(i, dirnames));
    }
    dirnames.erase(dirnames.begin(), dirnames.end());
  }
  return m2;
}

However, now my output is as follows:

X : DirA
Y : DirA
YY : DirBB
Z : DirA
ZZ : DirCC

I need it to be:

X : DirA, DirB, DirBB

etc. given that the file X is in DirA, DirB and DirBB. Therefore, I have to alter the function FileMap.


Solution

  • Your attempt in doing as described in comments is incorrect. It should look like this:

    std::map<std::string, std::vector<std::string>> 
    FinalMap(std::map<std::string, std::vector<std::string>>const& folderToFiles)
    {
        std::map<std::string, std::vector<std::string>> fileToFolders;
    
        for (auto const& pr : folderToFiles)
        {
            for (auto const& file : pr.second)
                fileToFolders[file].push_back(pr.first);
        }
    
        return fileToFolders;
    }
    

    That's it. This enumerates each mapping of folder-to-filesand creates a new mapping of file-to-folders. I'm pretty sure, was what you were looking for.