Search code examples
c++stringfiledictionaryc++98

Storing data in map<string, vector<std::pair<string, string>>> c++98


I have file named Bird.lst. I am trying to read its contents and store the data in a map<string, vector<pair<string, string>>>. The idea is to store the bird's name in the string and its attribute values in the vector.

Also, there are some attributes that I don't need (vaccinated, babies, sale). While inserting into the map, I have to check to not insert these attributes.

Where the attributes didn't got added, but while displaying, the map contents there are displaying empty spaces. I want to get rid of the empty lines from the map.

My map content should be seen as below. please help.

parrot.sh   ---->  eat    yes
                   fly    yes

Bird.lst

parrot.sh
vaccinated  yes
eat         yes
babies      no
fly         yes
sale        no

pigeon.sh
vaccinated  yes
eat         yes
fly         yes
babies      yes
sale        yes

duck.sh
vaccinated  yes
eat         yes
fly         no
sale        yes
babies      no

flammingo.sh
vaccinated  yes
eat         yes
fly         yes
sale        no
babies      no

eagle.sh
vaccinated  yes
eat         yes
babies      no
fly         yes

Code:

#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include <utility>

typedef std::pair<std::string,std::string> attribute_pair;
typedef std::vector<attribute_pair> attribute_vector;
typedef std::map<std::string,attribute_vector> bird_map;

int main()
{
    std::ifstream file("Bird.lst");

    bird_map birds;
    std::string key;
    while(std::getline(file,key))
    {
        attribute_vector attributes;
        std::string value;
        while(std::getline(file,value))
        {
            // in case it has windows encoding with end-of-line = \r\n
            if (!value.empty() &&
                value[value.size()-1] == '\r')
            {
                value.erase(value.size() - 1);
            }

            // if we found the empty string
            if(value.empty())
            {
                break;
            }

            // now split the value into an attribute and a flag
            attribute_pair attribute;
            std::istringstream ss(value);
            if(value.find("vaccinated") == std::string::npos && value.find("babies") == std::string::npos && value.find("sale") == std::string::npos)
            ss >> attribute.first >> attribute.second;

            // save the value into the vector
            attributes.push_back(attribute);
        }
        // save the bird into the map
        birds[key] = attributes;
    }

    // now print the data we collected
    for(bird_map::iterator bird = birds.begin();
        bird != birds.end();
        bird++)
    {
        std::cout << bird->first << "\n";
        for(attribute_vector::iterator attribute = bird->second.begin();
            attribute != bird->second.end();
            attribute++)
        {
            std::cout << "   " << attribute->first
                      << "   " << attribute->second
                      << "\n";
        }
        std::cout << "\n";
    }

    return 0;
}

Output from the above code:

duck.sh

   eat   yes
   fly   no



eagle.sh

   eat   yes

   fly   yes

flammingo.sh

   eat   yes
   fly   yes



parrot.sh

   eat   yes

   fly   yes


pigeon.sh

   eat   yes
   fly   yes


Solution

  • When you are parsing a value string into an attribute_pair, you are looking for specific attributes to ignore, and if you find one then you don't parse the value, but you are still inserting the empty attribute_pair into the attributes vector. That is where the empty lines in your output are coming from.

    Change this:

    // now split the value into an attribute and a flag
    attribute_pair attribute;
    std::istringstream ss(value);
    if(value.find("vaccinated") == std::string::npos && value.find("babies") == std::string::npos && value.find("sale") == std::string::npos)
    ss >> attribute.first >> attribute.second;
    
    // save the value into the vector
    attributes.push_back(attribute);
    

    To this instead:

    // now split the value into an attribute and a flag
    attribute_pair attribute;
    std::istringstream ss(value);
    ss >> attribute.first >> attribute.second;
    if(attribute.first != "vaccinated" && attribute.first != "babies" && attribute.first != "sale") {
        // save the value into the vector
        attributes.push_back(attribute);
    }
    

    Online Demo

    That being said, you might consider using a map<string,string> instead of a vector<pair<string,string>>, unless you have a requirement to preserve the original order of the attributes of each bird (you are not preserving the order of the birds themselves, since std::map is sorted).