Understanding what QHash does when key not found - c++

Note: You can find a minimal working example at the end of this post.
I'm using Qt 5.7. Let's say I have the following QHash:
QHash<HashKey, HashValue> hm;
with
enum HashKey {
K1,
K2,
K3,
K4,
K5
}
and
class HashValue {
public:
int x;
HashValue(int x) {
this->x = x;
}
}
I have initialized the hash map like this:
hm.insert(K1, HashValue((int)K1));
hm.insert(K2, HashValue((int)K2));
hm.insert(K3, HashValue((int)K3));
hm.insert(K4, HashValue((int)K4));
hm.insert(K5, HashValue((int)K5));
I have tested it by calling
cout << hm.value(K4).x << endl;
cout << hm.find(K4).value().x << endl;
Both return the same result that is 3. Now I tried doing the same with a key that is not part of the hash map by casting an integer to HashKey and calling the above two methods on it:
cout << hm.value(static_cast<HashKey>(100)).x << endl;
cout << hm.find(static_cast<HashKey>(100)).value().x << endl;
What I got is 8 (for the first call with value().x) and 5 (for the second call with find(...).value().x)
The docs states that
If there is no item with the specified key in the hash, these
functions return a default-constructed value.
I followed the link for default-constructed value and got the following:
[...] for example, QVector automatically initializes its items with
default-constructed values, and QMap::value() returns a
default-constructed value if the specified key isn't in the map. For
most value types, this simply means that a value is created using the
default constructor (e.g. an empty string for QString). But for
primitive types like int and double, as well as for pointer types, the
C++ language doesn't specify any initialization; in those cases, Qt's
containers automatically initialize the value to 0.
In my case this would mean a HashValue() call. However the fact that I get different results is baffling to say the least. I would expect to get the same result though the docs don't mention what find(...) does when an invalid key is passed as argument. All it says it finds the first occurrence of that key and returns an iterator (obviously since I call value() on it in the call above).
The quoted doc snippet from above is followed (again back to the document for QHash) by
If you want to check whether the hash contains a particular key, use
contains()
I can deal with having to call contains() every time I query my hash map though this means making two function calls - first to check if key is present and then to call value(...) to get the actual value if a valid entry is found. The call below returns "Key 100 not found":
cout << (hm.contains(static_cast<HashKey>(100)) ? "Key 100 found" : "Key 100 not found") << endl;
I would expect this check to be done internally but obviously this doesn't happen (my guess would be to prevent some performance impact on the querying functionality of this container).
The question here is why is all this happening and what is actually happening underneath all that?
Here is the project and the code for it:
HashTest.pro
QT += core
QT += gui
CONFIG += c++11
TARGET = HashTest
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
main.cpp
#include <QCoreApplication>
#include <QHash>
#include <iostream>
using namespace std;
enum HashKey {
K1 = 0,
K2 = 1,
K3 = 2,
K4 = 3,
K5 = 4
};
class HashValue {
public:
int x;
HashValue(int x) { this->x = x; }
HashValue() {}
};
int main(int argc, char *argv[])
{
QHash<HashKey, HashValue> hm;
hm.insert(K1, HashValue((int)K1));
hm.insert(K2, HashValue((int)K2));
hm.insert(K3, HashValue((int)K3));
hm.insert(K4, HashValue((int)K4));
hm.insert(K5, HashValue((int)K5));
cout << hm.value(K4).x << endl;
cout << hm.value(static_cast<HashKey>(100)).x << endl;
cout << hm.find(K4).value().x << endl;
cout << hm.find(static_cast<HashKey>(100)).value().x << endl;
cout << (hm.contains(static_cast<HashKey>(100)) ? "Key 100 found" : "Key 100 not found") << endl;
return a.exec();
}

The value() function is basically just for accessing values not checking if you have a one.
It returns a value and there is no way to indicate whether the value is "invalid" or not. So the choice if the design was to construct one. Qt could as an alternative throw an exception but this is not done here for several reasons (same as the containers of the c++ standard library btw.).
Secondly:
You are kind of using find() in a wrong way.
With find you can check whether the key is in the list and if not it point to the end() iterator of the hash.
QHash< Key,Value >::const_iterator valueIt = hash.find(<something>)
if(valueIt == hash.end())
{ // not found. error handling etc.
}
Value value = valueIt.value();
This is usually the "standard" way to check if a key exists and access it in a Map/Hash/Set/....
So when you use
find(...).value();
you could possibly access the end() iterator which causes undefined behavior.

Related

Is there a way to make selecting a specific variable more efficient?

So I'm writing a program that has 9 different mazes in it stored in 2d arrays all filled with hard coded values. When the player chooses the maze, I want to copy the hard coded values from the maze selected into the 2d array of the active maze. When I wrote it out I did it in the most straightforward way possible as you can see below. Then I wanted to maze it better as it seems... bloated. A switch case wouldn't reduce the amount of lines, so I wanted to make some way to immediately put the int mazeSelection variable into the variable name of the maze selected. But it seems you can't alter a variable name during runtime, nor use a string variable to represent the name of another variable. For example string mazenumber = "maze" + tostring(mazeSelection); then doing mazenumber[11][11] doesn't work, but that's the basic idea of what I want to do.
So the upshot is, is there a way to make this code more efficient?
if(mazeSelection == 1)
maze[11][11] = maze1[11][11];
if(mazeSelection == 2)
maze[11][11] = maze2[11][11];
if(mazeSelection == 3)
maze[11][11] = maze3[11][11];
if(mazeSelection == 4)
maze[11][11] = maze4[11][11];
if(mazeSelection == 5)
maze[11][11] = maze5[11][11];
if(mazeSelection == 6)
maze[11][11] = maze6[11][11];
if(mazeSelection == 7)
maze[11][11] = maze7[11][11];
if(mazeSelection == 8)
maze[11][11] = maze8[11][11];
if(mazeSelection == 9)
maze[11][11] = maze9[11][11];
So your question is lacking in detail, but lets assume you have this
int maze[11][11], maze1[11][11]; // etc
Then the first thing to say is that
maze[11][11] = maze1[11][11];
does not copy your maze. It's a very common beginner misunderstanding that you can refer to a whole array this way, but maze1[11][11] just refers to one element of the maze at coordinates (11,11) not to the whole maze. And worse since the size of the array is 11 by 11, that element doesn't actually exist, so the code is just an error. There is (surprisingly) no simple way to copy an array in C++.
The simplest suggestion (thanks to #molbdnilo) is to put your maze inside a struct.
struct Maze
{
int tiles[11][11];
};
Maze maze, maze1; // etc
Now structs can be copied in the usual way, so
maze = maze1;
is legal code and does copy the maze.
Then you can go further and make an array of mazes, and write this simple code
Maze selected_maze, all_mazes[10];
selected_maze = all_mazes[mazeSelection];
Easiest way for your case is to store needed to choose values in vector and then just index that vector. I.e. instead of writing
if (x == 0)
y = z0;
else if (x == 1)
y = z1;
else if (x == 2)
y = z2;
you do
static vector<ValueT> values = {z0, z1, z2, ValueT()/*no answer for 3*/, z4, z5};
if (x < values.size())
y = values[x];
else
y = ValueT(); // No value found!
Note that above solution works good if keys to check (inside if condition) cover densely whole vector's range [0;size), if some values are absent you may store in corresponding vector's slots special values signifying that there is no answer.
If keys-space is to sparse then there will be too many no-value elements in vector and this solution may be to memory-wasteful, then next other solutions will do. But this vector-solution is the fastest regarding speed of getting right value by given key.
In more difficult cases, when you need to have arbitrary expression inside if condition or when you need to run arbitrary code in if body, then you need to use more advanced solutions like those that I've just coded into code below (I've coded all cases in growing complexity order). All these solutions are implementing fastest way to make if/then choice.
I'll explain code a bit.
When if condition just checks for equality to number in range [0;size) and result is just a value then we use vector. vector's values can be plain objects to return, or functional objects to be run (in case of complex handlers that are inside if-body). This choice works in constant O(1) time, i.e. very fast (see Time Complexity).
If keys to compare to are sparse (e.g. numbers 10, 20, 30, 40) then we use unordered_map or map, map can be used for any keys that are orderable (for map) or support equality and hashable (for unordered_map). This solution works in O(1) time for unordered_map, i.e. very fast (but constant may be not very small), use unordered_map if you have dozens of if-cases. For map it works in O(log(N)) time (N is number of handlers/if-bodies), so is also very fast, use it for cases below dozen. map is faster than unordered_map for small number of cases.
For the most complex case when if-conditions are complex expressions and if-bodies are also complex code-blocks, i.e. when we have func-func mapping, can be also solved fast in logarithmic time (O(log(N))) but only for the case if all if-cases can be ordered (sorted) in such a way that for each current if-case we can definitely tell if the correct matching case probably (if exists at all) lies in handlers to the left from current (flag -1) or that current case is matching one (flag 0) or that correct matching case probably (if exists at all) lies in handlers to the right from current. In other words all handlers can be ordered in one definite way for all possible input arguments (of if-condition expression-func). In this case we just do a logarithmic-time Binary Search using std::lower_bound(...).
So all recommendations are:
If keys are non-negative integers (or keys can be mapped to such integers via some simple function) and if this integers space is not too sparse (otherwise vector-solution is memory-wasteful) then use std::vector for mapping. Fetching from vector by index is O(1) time with small constant time (several CPU instructions), i.e. very fast.
If there are very many keys (more than hundreds) and keys are equality-comparable and hash-able then use std::unordered_map. Fetching time by key is O(1) for unordered time, but with not-so-small constant time (hundred of CPU instructions), i.e. also very fast and fetch time doesn't grow with number of map elements.
If keys are not too many (below hundred) and keys are fully-orderable (i.e. can be sorted) then use std::map. It has O(log(N)) time with small constant, i.e. also very fast.
If there are no keys, i.e. if-conditions are complex expressions and there are many (more than dozen) of if cases then use std::vector of sorted pairs of functions representing a pair of (if_condition_code_matcher, if_body_code). Searching matching cases would need O(log(N)) number of if-condition-code evaluations, i.e. also very fast.
If there are very few (below 5-6) if-cases or if you don't need speed or when if-cases-handlers can't be sorted regarding all arguments or rules 1-5 don't apply or you simply don't want any complex solutions then use just plain set of ifs.
Rules 1-5 are all about different ways of fast finding matching if-case. Regarding values - all structures above can store any type of value. So:
Store regular objects as structure's values (int, string, or any class object) if your if-cases where just returning some data without any code-computation, like in your case. Just return this value after obtaining structure's slot by key.
Store functions as structure's values if your if-bodies contains complex code. After fetching by key just run this function-value as a handler.
Also in case of keys being integers (or map-able to them) like in case 1 you can also use old-good switch-case. Clever compilers optimize code in such a way that they use only goto commands for jumping to matching if-body. But for this case you need to have all cases within switch being ordered, and also all values for range [0;size) should be covered in switch's cases. But such optimization is not guaranteed, compiler still may do regular sequential if-condition-trying. Hence vector solution is the only guaranteed optimization.
Code below can also be run online here.
#include <unordered_map>
#include <functional>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <utility>
#include <algorithm>
using namespace std;
#define ASSERT_MSG(cond, msg) { if (!(cond)) throw runtime_error("Failed assertion [" #cond "]! Msg: " + string(msg)); }
typedef string ValT;
// Makes choice in fixed O(1) time.
// Suitable only for having possible mapping for all keys in range [0;size).
ValT const & HandleValToValVec(size_t i) {
static std::vector<ValT> handlers = {"zero", "one", "two", "three"};
if (i < handlers.size()) {
ValT const & val = handlers[i];
cout << "Key " << i << " was mapped to \"" << val << "\"" << endl;
return val;
} else {
cout << "Key " << i << " has no mapping!" << endl;
static ValT null_val;
return null_val;
}
}
typedef size_t KeyT;
// Makes choice in fixed O(1) time.
// Suitable for any comparable keys or if keys space is sparse (not covering range [0;size)).
ValT const & HandleValToValMap(KeyT const & key) {
static std::unordered_map<KeyT, ValT> handlers = {{10, "ten"}, {20, "twenty"}};
auto it = handlers.find(key);
if (it == handlers.end()) {
cout << "Key " << key << " has no mapping!" << endl;
static ValT null_val;
return null_val;
} else {
cout << "Key " << key << " was mapped to \"" << it->second << "\"" << endl;
return it->second;
}
}
// Makes choice in fixed O(1) time.
void HandleValToFunc(KeyT const & key) {
// Handlers containing any arbitrary code, "static" here is important to re-create array only once.
static std::unordered_map< KeyT, function<void()> > handlers = {
{KeyT(10), [&](){
cout << "Chosen Key 10" << endl;
}},
{KeyT(15), [&](){
cout << "Chosen Key 15" << endl;
}},
};
auto it = handlers.find(key);
if (it == handlers.end())
cout << "No Handler for Key " << key << endl;
else
it->second();
}
typedef string ArgT;
// Makes choice in logarithmic O(log(N)) time, where N is number of handlers.
void HandleFuncToFunc(ArgT const & arg0) {
// Handlers containing any arbitrary code, "static" here is important to re-create array only once.
// First function in handlers's pair should return -1 if matching handler probably lies to the left,
// 0 if this handler is matching, 1 if probably lies to the right.
static std::vector< pair< function<int(ArgT const &)>, function<void()> > > handlers = {
{[](ArgT const & arg0)->int{
return arg0.size() < 3 ? -1 : arg0.size() < 5 ? 0 : 1;
}, [&](){
cout << "Chosen String with len within range [3;5)." << endl;
}},
{[](ArgT const & arg0)->int{
return arg0.size() < 6 ? -1 : arg0.size() < 8 ? 0 : 1;
}, [&](){
cout << "Chosen String with len within range [6;8)." << endl;
}},
};
auto it = std::lower_bound(handlers.begin(), handlers.end(), arg0, [](auto const & handler, ArgT const & arg0) {
return handler.first(arg0) > 0;
});
if (it == handlers.end() || it->first(arg0) != 0)
cout << "No Handler for String \"" << arg0 << "\"" << endl;
else
it->second();
}
int main() {
try {
HandleValToValVec(0); HandleValToValVec(3); HandleValToValVec(5);
HandleValToValMap(10); HandleValToValMap(20); HandleValToValMap(30);
HandleValToFunc(10); HandleValToFunc(15); HandleValToFunc(20);
HandleFuncToFunc("ab"); HandleFuncToFunc("abcd"); HandleFuncToFunc("abcde"); HandleFuncToFunc("abcdef"); HandleFuncToFunc("abcdefgh");
return 0;
} catch (exception const & ex) {
cerr << "Exception: " << ex.what() << endl;
return -1;
}
}

QJsonArrays are not properly retrieved from QJsonObject

In the project I'm currently working on there I use Qt's JSON functionality to store the state of a graph, where every component of the system recursively calls the toJson-functions of its members for serialization. This works fine, but I run into a weird issue when deserializing the JSON file.
As a test that illustrates the problem, I've created the following example code:
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtDebug>
int main() {
auto document{QJsonDocument::fromJson("{\n"
" \"o1\" : {\n"
" \"i1\" : [\"l1\", \"l2\", \"l3\"],\n"
" \"i2\": 3.14\n"
" },\n"
" \"o2\" : {\n"
" \"i2\": 2.718,\n"
" \"i1\" : [\"l1\", \"l2\", \"l3\"]\n"
" }\n"
"}")};
qDebug() << "is the document an object:" << document.isObject();
auto document_object{document.object()};
auto object_1_value{document_object.value("o1")};
qDebug() << "is o1 an object:" << object_1_value.isObject();
auto object_1{object_1_value.toObject()};
auto inner_1_value{object_1.value("i1")};
qDebug() << "is i1 an array:" << inner_1_value.isArray();
auto inner_1{inner_1_value.toArray()};
qDebug() << "i1:" << inner_1;
auto inner_1_inner_value{inner_1.at(0)};
qDebug() << "is i1[0] an array:" << inner_1_inner_value.isArray();
auto inner_1_inner{inner_1_inner_value.toArray()};
qDebug() << "i1[0]:" << inner_1_inner;
return 0;
}
Here, I am first querying o1, then try to get the array stored under i1.
However, I get the following output:
is the document an object: true
is o1 an object: true
is i1 an array: true
i1: QJsonArray([["l1","l2","l3"]])
is i1[0] an array: true
i1[0]: QJsonArray([["l1","l2","l3"]])
It seems like Qt stores the returned array into a useless one-element array; in my other code, at(0) solved the issue, but here even that does not work.
I would be very glad if someone could help me find a solution to reliably (and preferably hacklessly) read nested JSON arrays with Qt, as I truly do not understand the issue.
I am on Linux 5.6.11 with gcc 9.3.0 and Qt 5.14.2.
Your problem is the brace initialization. When you do assignment-style initialization instead, it works.
// instead of
auto inner_1{inner_1_value.toArray()};
// use
auto inner_1 = inner_1_value.toArray();
// same for all other brace inits
What I think happens is the classic clash between brace initialization and list initialization via a std::initializer_list constructor, in this case this one:
QJsonArray::QJsonArray(std::initializer_list<QJsonValue> args);
You want to brace-init a QJsonArray, but std::initializer_list takes precedence and what you actually instantiate is a QJsonArray with one item which is again a QJsonArray.
Often you see this problem explained with std::vector<int> and the two clashing ctors:
vector(size_type count);
vector(std::initializer_list<int> init);

How do you determine what kind of node you are dealing with in yaml-cpp?

I'm reading the tutorial code here: https://code.google.com/p/yaml-cpp/wiki/Tutorial
One example goes like this:
YAML::Node primes = YAML::Load("[2, 3, 5, 7, 11]");
for (YAML::const_iterator it=primes.begin();it!=primes.end();++it) {
std::cout << it->as<int>() << "\n";
}
And the next like this:
YAML::Node lineup = YAML::Load("{1B: Prince Fielder, 2B: Rickie Weeks, LF: Ryan Braun}");
for(YAML::const_iterator it=lineup.begin();it!=lineup.end();++it) {
std::cout << "Playing at " << it->first.as<std::string>() << " is " << it->second.as<std::string>() << "\n";
}
However, if you swap the YAML files between these two cases, you will get an error, as you are accessing a map iterator for a sequence or vice versa:
terminate called after throwing an instance of 'YAML::InvalidNode'
what(): yaml-cpp: error at line 0, column 0: invalid node; this may result from using a map iterator as a sequence iterator, or vice-versa
For an arbitrary YAML input, how can I determine whether I am dealing with a sequence or a map in the loop (ie whether I should be using ->first or not) without using a try/catch block?
I tried looking for the documentation, but I could not find it.
UPDATE:
This is what I am trying to do:
YAML::Node config = YAML::LoadFile(filename);
for (YAML::const_iterator it=config.begin();it!=config.end();++it) {
if (it->Type() == YAML::NodeType::Map) { // exception
std::cout << it->first.as<std::string>();
} else if (it->Type() == YAML::NodeType::Sequence) {
std::cout << it->as<std::string>();
}
}
But when I run the code I get the exception as above. It compiles fine.
I am using the yaml-cpp which comes with ubuntu 14.04 (0.5.1).
You can either
switch (node.Type()) {
case Null: // ...
case Scalar: // ...
case Sequence: // ...
case Map: // ...
case Undefined: // ...
}
or query explicitly, e.g.:
if (node.IsSequence()) {
// ...
}
(I added this bit to the Tutorial.)
Edit: In your specific example, you should check config.Type() before you iterate, not the type of any of the nodes during your iteration.

C++ Map returning class object based on variable key

I reacently learned about map structures and I am trying to use one, but can't seem to solve one problem.
I have tried the code below:
map<string, valuePair> translator;
The class valuePair is just a combination of 2 objects (string and a number).
Im assigning values to the map
translator[currentWord] = valuePair(stateNo, "state");
Where currentWord is a variable string, stateNo is an int.
Now later I want to get back the valuePair number value from the map, but can;t seem to be able to do it.
Heres a screenshot of my watch window trying to access the variable x.
http://i.imgur.com/m3MOgi2.png
These are all the ways I managed to find online to return the value, yet none of them seem to work. As you can see the key "a" is in the map. What am I doing wrong?
[EDIT] Thanks guys, I used the tips you gave in comments and found out, that actually it works the way I expected - translator["a"].x prints the values I need. I have nothing to mark as "Correct answer" though, and I'm not sure what to do with this thread now :/
As Jonathan Henson posted in the comments, you would be better off posting your code than your debugger output. Ideally you want a minimal example that reproduces the error you are having.
Based on the debugger output, I'm wondering if you have a scope issue -- you are trying to access the map data outside of the scope where you have it defined. Without seeing the source code though, there is no way to know.
Here is a working example that does precisely what you are trying to do. The only modification is I have used a struct for valuePair, and c++ 11 initializer lists. This won't affect the map access code, but you might need to turn on c++ 11 support to compile it.
As a first step, look check it out in your debugger and see if you get the same difficulty. If so, your problem is the debugger or debugger setup, not your code.
If the debugger works for the sample code (posted below), gradually transform my code into your code (making minimal changes, building, and see if it still works). This is a very useful approach to learning the fine points of a language.
#include <iostream>
#include <map>
#include <string>
using namespace std;
struct valuePair
{
int num;
string str;
};
int main()
{
map<string, valuePair> translator;
translator["a"] = {0,"bla"};
translator["b"] = {1, "alb"};
translator["c"] = {2, "lab"};
valuePair res = translator["c"];
cout << "c:" << res.num << "," << res.str << "\n";
res = translator.at("b");
cout << "b:" << res.num << "," << res.str << "\n";
res = translator.find("a")->second;
cout << "a:" << res.num << "," << res.str << "\n";
return 0;
}
If you have an std::map<K,V> m; you can add elements and change values by using m[k] = v;. However the operator[] is an operation which always creates a key/value pair, if the key you are looking for is not contained in the map. Thus it is not allowed when you have a const reference or pointer, e.g. const std::map<K,V>&.
With a std::map you always have to consider the case that the key you are looking for is actually not contained in the map!
To look for the value stored under a given key you have to use std::map::find (link).
Example:
std::map<std::string,valuePair>::const_iterator it = translator.find(currentWord);
if(it != translator.end()) {
// the map contains an element with this key
const valuePair& value = it->second; // this is the value
}
else {
// the map *does not* contain an element with this key
}
As mentioned in the comments std::map::at (link) may be an alternative for C++11. But then you have to take care of the possible exception which is thrown when you use a key which does not exist in the map.
this works for me
#include <map>
#include <sstream>
#include <string>
int main()
{
std::map<std::wstring, double> items;
items[L"0"] = 0.123;
items[L"1"] = 1.234;
items[L"2"] = 2.234;
items[L"3"] = 3.345;
items[L"4"] = 4.567;
for (int i = 0; i < 5; i++)
{
std::wstringstream oss;
oss << i;
std::wstring key = oss.str();
double value = items[key];
}
return 0;
}

Search vector of objects by object data member attribute

I'm writing a Jukebox simulator and I'm trying to search a vector of Album objects by album title and return the index for use in other functions. The function is to be used for a number of different things such as deleting an album, printing an album etc.
I have gotten it to work in a previous application when the function was in the same Class as the data member to search for. I can however for some reason not get it to work using getters. No matter what I input as search key the idx returns 3 although the vector only contains indexes 0, 1 and 2 (only 3 albums right now).
The lambda function seem to be able to access data by using the getAlbum()-getter but somehow the comparison doesn't work.
My approach might be entirely wrong and I'd be grateful for any pointers in the right direction, or suggestions on how to accomplish the desired result using some other technique.
int Jukebox::findAlbumIdx(string key)
{
// Get search predicate
auto it = find_if(albvec.begin(), albvec.end(), [key](Album const &a)
{
return (a.getAlbum() == key);
});
int idx = it - albvec.begin();
return idx;
}
void Jukebox::delAlbum()
{
cin.get();
string key;
cout << "Input title of album to delete: ";
getline(cin, key);
int idx = findAlbumIdx(key);
if(idx > albvec.size() - 1)
cout << "No hits." << endl;
else
albvec.erase(albvec.begin() + idx);
}
getAlbum is just a simple inline getter as such:
string getAlbum() const {return album_title;}
Following Jonathan Wakely's suggestion to add std::cout << a.getAlbum() << " == " << key << std::endl; in the lambda the output is this:
Input title of album to delete: Abbey Road
== Abbey Road
== Abbey Road
== Abbey RoadLonely Hearts Club Band
No hits.
Obviously the getter isn't actually getting much to use for comparison. Not sure why it only gets the last entry and on the right hand side of the comparison.
If I add this to any of the functions above it gets and displays the Album titles correctly. The vector seems to be fine just before calling findAlbumIdx(key); and also inside the findAlbumIdx function.
for(unsigned int i = 0; i < albvec.size(); ++i)
cout << albvec[i].getAlbum() << endl;
The original playlist file that is read into the vector to be searched had dos newlines, after converting it using dos2unix (since I'm running Linux) the search, and I presume a lot of other things, is working correctly.
I suppose trimming newline characters while reading the file into the vector would be the more correct approach.