Attribute Parser

  • + 5 comments

    Although the problem states "We can call an attribute by referencing the tag, followed by a tilde, '~' and the name of the attribute", there are queries without tilde (~) (test case #4, #5). So be careful when parsing your queries. I think the author should describe the problem more clear.

    • + 1 comment

      Hi, How did you know the content of the test cases? And without the tilde how did the test cases specify the attribute name after a tag?

      • + 2 comments

        It's simple. Use your hackos to buy the test cases. One test case costs you 5 hackos.

        • + 1 comment

          How? Could you point out how to do this? Many thanks.

          • + 1 comment

            First you should have enough hackos to buy test cases (5 hackos/ testcase). Too see how many hackos you have currently, click on your login ID on the top right corner.You can click on 'Hackos' hyperlink to learn more about how to earn and use these hackos.

            Now suppose that you have submit your solution and got some test cases which are failed. Move your mouse over a failed test case, you should see a popup message. Click the 'Download' hyperlink and you will see 'Purchase TestCase' window. Click yes to buy it. Then you will see 'Download TestCase' window. Click 'Input' or 'Output' to download the content of Input or Expected Output data respectively. Hope this helps.

            • + 1 comment

              Oh crud... I thought you earned 5 hackos by running your submitted code against a special "hidden" test case. Gees... I wonder how much I've wasted.

              Thanks!

              • + 1 comment

                here is solution of problem Attribute Parser in c++ programming https://solution.programmingoneonone.com/2020/06/hackerrank-attribute-parser-solution-c-plus-plus.html

                • + 0 comments

                  Don't paste links of the answer .

        • + 0 comments

          the attribute without ~ should not be printed right???

    • + 2 comments

      Hi! So what is the correct behavior for queries without tilde? I guess there are some more inaccuracies in the descreption as I have the following custom input which works great, but any test case other than default fails:

      12 8
      <tag1 value = "HelloWorld">
      	<tag2 name = "Name1">
      		<tag3 name = "Name3">
      		</tag3>
      		<tag4 name = "Name4" text = "Super">
      			<tag6 buzz = "Buzz6">
      			</tag6>
      		</tag4>
      		<tag5>
      		</tag5>
      	</tag2>
      </tag1>
      tag1.tag2~name
      tag1~name
      tag1~value
      tag1.tag2.tag3~name
      tag1.tag2.tag3~value
      tag1.tag2.tag4~text
      tag1.tag2.tag4.tag6~text
      tag1.tag2.tag4.tag6~buzz
      

      As you see it has multilevel nesting, multiple attributes support, no attributes support (tabs are added for better visibility). Confused...

      • + 5 comments

        Some of the Test Cases contain queries that have a . instead of the ~
        These still expected to produce "Not Found!". For your example input, try the queries:

        tag1.value
        tag1.tag2.name
        

        Also make sure you can handle attribute names with underscores in them and attribute values containing characters like $,.!

        Very poor that the question didn't specify all this. It took me hours to figure out what was wrong.

        • + 0 comments

          Thanks a lot for the clarification!

        • + 1 comment

          if '~' is replaced with '.' in query string, we can modify processing of query like this:

          void process_queries(int q, std::map <std::string, std::string> &attributes)
          {
              std::string str;
              for (int i = 0; i < q; i++)
              {
                  getline(std::cin, str);
                  if (attributes.find(str) == attributes.end())
                  {
                      size_t pos = str.find("~");
                      if (pos != std::string::npos)
                      {
                          str[pos] = '.';
                          if (attributes.find(str) != attributes.end())
                          {
                              std::cout << attributes[str] << "\n";
                          }
                      }
                      std::cout << "Not Found!\n";
                  }
                  else
                  {
                      std::cout << attributes[str] << "\n";
                  }
              }
          }
          
          • + 0 comments

            If attributes are encoded as tag1[.tagN]~attr , searching for tag1[.tagN].attr would fail because there would never be a match. Surely you meant the opposite -- basic_string<char>.rfind(".") and converting that to ~

      • + 0 comments

        it should be like this?

        Name1

        Not Found!

        HelloWorld

        Name3

        Not Found!

        Super

        Not Found!

        Buzz6

    • + 0 comments

      Test data and problem statement is modified now.

    • + 17 comments

      Using MAP STL:

      int n,q;
      cin >> n >> q;
      cin.ignore();
      
      map <string,string> attributeDB; // to store attribute-value pairs
      string inputstr,tag_preamble="";
      
      // get each HRML line
      for (int i=0;i<n;i++) {
          getline(cin,inputstr);    
      
          // for each HRML line, break it up into token words
          stringstream ss(inputstr);
          string word, attribute, value;
          size_t pos;
          while (getline(ss, word, ' ')) { // for each token word
              // tag detected -> adjust tag_preamble by +/- tag
              if (word[0]=='<') {
                  string tag;
                  if (word[1]=='/') { // it's tag closing
                      tag=word.substr(2);   
                      tag=tag.substr(0,tag.length()-1); // rid of ">"
                      pos=tag_preamble.find("."+tag);
                      if (pos==string::npos) tag_preamble="";
                      else tag_preamble=tag_preamble.substr(0,pos);
                  }
                  else { // it's tag opening
                      tag=word.substr(1);
                      if (tag.find(">")!=string::npos) 
                      tag=tag.substr(0,tag.length()-1); // rid of ">"
                      if (tag_preamble=="") tag_preamble=tag;
                      else tag_preamble=tag_preamble+"."+tag;
                  }
              }
              // value detected
              else if (word[0]=='"') {
                  pos=word.find_last_of('"');
                  value=word.substr(1,pos-1);
                  attributeDB[attribute]=value; // insert into DB
              }
              // attribute name detected
              else if (word[0]!='=') {
                  attribute=tag_preamble + "~" + word;  
              }
          }
      }
      
      // now we process the queries
      for (int i=0;i<q;i++) {
          getline(cin,inputstr); 
          if (attributeDB.find(inputstr)==attributeDB.end())
              cout << "Not Found!" << endl;
          else
              cout << attributeDB[inputstr] << endl;
      }
      
      • + 0 comments

        your code is simple and light

      • [deleted]
        + 0 comments

        Wow! Thank you!

      • + 1 comment

        what does your 'tag_preamble=""' do in the code? Could you explain a bit?

        • + 0 comments

          It clears the value of the string 'tag_preamble' :p Are you curious to know, what is the function of 'tag_preamble'? Try to figure out what are the keys in the map 'attributeDB'. You can analyse, how this map is built, or you can first take a look, how it's used:

          for (int i=0;i<q;i++) {
              getline(cin,inputstr); 
              if (attributeDB.find(inputstr)==attributeDB.end())
                  cout << "Not Found!" << endl;
              else
                  cout << attributeDB[inputstr] << endl;
          }
          
      • + 0 comments

        pos=tag_preamble.find("."+tag); if (pos==string::npos) tag_preamble=""; else tag_preamble=tag_preamble.substr(0,pos);

        can anyone explains what this piece of code do actually im new to c++

      • + 1 comment

        what if two tags are not nested example

        <tag1 att_name="att_value">
        </tag1>
        <tag2>
        </tag>
        

        is this case is included in the hackerank testcases?

        • + 0 comments

          Yes, it still works. Because it resets "tag_preamble" when the closed tag is encountered:

          pos=tag_preamble.find("."+tag);
          if (pos==string::npos) tag_preamble="";
          else tag_preamble=tag_preamble.substr(0,pos);
          

          So, if it doesn't find the opening part of the tag in the format of "something.tag" (meaning it is a sub-tag) in the first line (so pos==string::npos), that means this is the primary tag, so it resests it to nothing (tag_preamble="").

          If it does find the tag in that format ("something.tag"), that means this is a sub-tag, so it erases the ".tag" part (leaving only the "something", which will be retained through future line iterations, as it is declared outside of the main for-loop), removing it from the pathway hierarchy for future elements when they are added to the map.

          The elements are added to the map based on the string generated from tag_preamble. This is how they are pulled for printing later. So any final values would have the correct, full, corresponding index "pathway" via the [string] (tab_preamble) used to access the map.

      • + 1 comment

        i am new to the concept of map. Can you please recommend me some tutorial or video to get started with?

        • + 0 comments

          Here

      • + 1 comment

        Why are we using size_t data type?

        • + 0 comments

          When you call string::find and when it doesn't match anything, it returns string::npos which equal to literally -1. The type of npos here is comparable to size_t.

      • + 0 comments

        You may get a performance increase if you use unordered_map as you are processing queries in order so the storage order doesn't matter.

      • + 0 comments

        I am new to these map concepts and couldn't figure out the use of "size_t pos", please can you elaborate.

      • + 0 comments

        Thank you for sharing!

      • + 0 comments

        the code works fine however anyone using this, must also make sure to use two additional header files: 1. map.h 2. bits/stdc++.h

        otherwise the compiler will show errors

      • + 0 comments

        how could distinguish a.b~value and b~value ?

      • + 0 comments

        I love the way you see the problem ,very clever and easy thinking

      • + 1 comment

        You can use a deque to keep track of the token stack and use a deque to string mapping function to resolve it to an attributeDB entry:

        string d_to_string(const deque<string> &d) {
            string s;
            for (const auto &t:d) {
                if (t[0] == '<') {
                    if (s.size())
                        s += '.';
                    s += t.substr(1);
                } else
                    s += '~' + t;
            }
            return s;
        }
        
        [...]
            cin.ignore();
            deque<string> tags;
        [...]
                   if (word[0] == '<') {
                        // tag::end -> pop
                        if (word[1] == '/') {
                            tags.pop_back();
                        // tag::begin -> push
                        } else {
        					// the "- (word[word.size()-1] == '>')" trick is needed to pass half the cases
        					// this is due to no-attr tags of the form "<tag>" and "<tag>" being in the deque
        					// instead of "<tag"
                            tags.push_back(word.substr(0, word.size() - (word[word.size()-1] == '>')));
                        }
                    // attr::val
                    } else if (word[0] == '"') {
                        size_t pos = word.find_last_of('"');
                        attr_db.insert({d_to_string(tags)], word.substr(1,pos-1)});
                        tags.pop_back();
                    // attr::name
                    } else if (word[0] != '=') {
                        tags.push_back(word);
                    }
        [...]
        
        • + 0 comments

          On second thought, a vector would probably be better here because the head to tail to traversal would have better locality. However, this would come with the downside that push_back would be only amoritized O(1) instead of strict O(1) unless capacity can be guessed from the maximum nesting level of tags (+1 for the attribute)

      • + 0 comments

        pos=tag_preamble.find("."+tag); if (pos==string::npos) tag_preamble=""; else tag_preamble=tag_preamble.substr(0,pos);

                                    can somebody explain me these three lines from the above solutions...
        
      • + 0 comments

        really helpful, thanks!

    • + 0 comments

      My code outputs Not Found! in case the query doesn't contain a ~. Is that what is expected out of #4 and #5 as these are the ones failing for my code.