Hash Tables: Ship Records

I'm currently writing a program that uses hash tables searching through ship records. It's based off a text file I named "shipRecords.txt" containing the following:

1
2
3
4
5
6
  1009 1 "Royal Queen"     2015 160
 1010   2  "Carnival"        2016  1600
 1019  1  "Ocean King"       2013  110
 1029 2 "Royal Prince"     2012 2000
 1039 2 "Royal Princess"  2010 2100
 1014 2 "Royal Caribbean" 2016 1600


The thing I'm having trouble with is displaying the records in the format as shown in the expected output below.

The displays are consisted of in 3 functions: displayAll(), displayOne(), and deleteOne().

displayAll()- For each bucket, it first displays a bucket number and then
lists all the records in the bucket. The system will display all records in a
listing.

displayOne()- With a given serial number, the bucket # and information of
the ship is displayed.

deleteOne()- Delete the record of the given serial number.

I have never used hash tables before, but if anyone could help me out or give me some hints to achieve the expected output, I would really appreciate it!


Code in progress...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
using namespace std;

struct ShipRecord
{
    int serialNum;
    int shipType;
    string name;
    int year;
    int cap;
    ShipRecord * link;
};

const int SIZE = 10;
class HashMgr
{
    ShipRecord * hashTable[SIZE] = {nullptr};

public:
    HashMgr()
    {
        string line;
        ifstream inputFile;
        inputFile.open("shipRecords.txt");

        if(inputFile.is_open())
        {
            while (!inputFile.eof())
            {
                getline(inputFile, line);
                addInfo(line);
            }
            inputFile.close();
        }
    }

    ~HashMgr()
    {
        /// To DO: Add your code here to ensure no memory leak occurs
        for (int i = 0; i < SIZE; ++i)
        {
            while (hashTable[i] != nullptr)
            {
                ShipRecord* temp = hashTable[i];
                hashTable[i] = temp->link;
                delete temp;
            }
        }
    }

    addInfo(string line)
    {
        //int i = 0;
        //size_t p1 = 0;
        size_t p2 = 0;
        string temp = line;

        cout << endl << "PARSING STRING:\"" << line << "\"" << endl;
        cout << "---------------" << endl;

        while(temp.length() > 5)
        {
            if(temp[0] == ' ')
            {
                temp = temp.substr(1);
            }
            else if(temp[0] == '"')
            {
                p2 = temp.find_first_of('"', 1);
                cout << temp.substr(1, p2 - 1) << endl;
                temp = temp.substr(p2 + 1);
            }
            else
            {
                p2 = temp.find_first_of(' ');
                cout << temp.substr(0, p2) << endl;
                temp = temp.substr(p2);
            }
        }

        cout << "---------------" << endl << endl;
    }

    displayOne(int serialNum)
    {
        /*
        for(int i = 0; i < SIZE; i++)
        {
            if(SIZE == serialNum)
            {
                cout << serialNum << endl;
            }
        }
        cout << serialNum << " <- This record does not exist!" << endl;
        */
    }

    displayAll()
    {

    }

    deleteOne(int serialNum)
    {
        /*
        for(int i = 0; i < SIZE; i++)
        {
            delete hashTable[i];
        }

        cout << serialNum << " <- Records deleted!" << endl;
        */
    }
};

int main()
{
    HashMgr hm;

    cout << "displayAll()" << endl << endl;
    hm.displayAll();

    cout << "displayOne()" << endl << endl;
    hm.displayOne(1009);
    hm.displayOne(1010);
    hm.displayOne(1019);
    hm.displayOne(1029);
    hm.displayOne(1039);
    hm.displayOne(1014);
    hm.displayOne(1008); /// Prompt a message to that the record does not exist

    //cout << "deleteOne()" << endl << endl;
    hm.deleteOne(1009);
    hm.deleteOne(1039);

    cout << "displayAll()" << endl << endl;
    hm.displayAll();

    return 0;
}


Current Output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

PARSING STRING:"  1009 1 "Royal Queen"     2015 160"
***********
1009
1
Royal Queen
2015
***********


PARSING STRING:" 1010   2  "Carnival"        2016  1600"
***********
1010
2
Carnival
2016
***********


PARSING STRING:" 1019  1  "Ocean King"       2013  110"
***********
1019
1
Ocean King
2013
***********


PARSING STRING:" 1029 2 "Royal Prince"     2012 2000"
***********
1029
2
Royal Prince
2012
***********


PARSING STRING:" 1039 2 "Royal Princess"  2010 2100"
***********
1039
2
Royal Princess
2010
***********


PARSING STRING:" 1014 2 "Royal Caribbean" 2016 1600"
***********
1014
2
Royal Caribbean
2016
***********

displayAll()

displayOne()

displayAll()


Expected Output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

PARSING STRING:"  1009 1 "Royal Queen"     2015 160"
***********
1009
1
Royal Queen
2015
***********


PARSING STRING:" 1010   2  "Carnival"        2016  1600"
***********
1010
2
Carnival
2016
***********


PARSING STRING:" 1019  1  "Ocean King"       2013  110"
***********
1019
1
Ocean King
2013
***********


PARSING STRING:" 1029 2 "Royal Prince"     2012 2000"
***********
1029
2
Royal Prince
2012
***********


PARSING STRING:" 1039 2 "Royal Princess"  2010 2100"
***********
1039
2
Royal Princess
2010
***********


PARSING STRING:" 1014 2 "Royal Caribbean" 2016 1600"
***********
1014
2
Royal Caribbean
2016
***********

displayAll()
Bucket #0
   1010 2 "Carnival"         2016  1600
Bucket #4
   1014 2 "Royal Caribbean"  2016  1600
Bucket #9  
   1009 1 "Royal Queen"      2015   160
   1019 1 "Ocean King"       2013   110
   1029 2 "Royal Prince"     2012  2000
   1039 2 "Royal Princess"   2010  2100

displayOne()
Bucket #0
   1010
Bucket #4
   1014
Bucket #8
   1008 <- This record does not exist!
Bucket #9  
   1009
   1019
   1029
   1039

displayAll()
Bucket #0
   1010 2  "Carnival"        2016  1600
Bucket #4
   1014 2 "Royal Caribbean"  2016  1600
Bucket #9  
   1019 1  "Ocean King"      2013   110
   1029 2 "Royal Prince"     2012  2000

Deleted ship record (1009)!
Deleted ship record (1039)!
Last edited on
> The thing I'm having trouble with is displaying the records in the format as
> shown in the expected output below.
well, your display() functions are all empty and you never filled the hashtable


> I have never used hash tables before
1
2
position = hash_function(ship_info)
hashTable[position].push_back(ship_info)

your hash_function() needs to somehow map your object to the range [0; SIZE)
for example ship_info.serialNum % SIZE (¿is this a good function?)

then you need to manage collisions, it seems that you are using a linked list in each bucket
i would recommend you to actually use a linked list and not just raw nodes.
@ne555; I updated my code by adding a hash function, changing my addInfo() function code, wrote the code that's supposed to output the display() functions. However, I came across another problem, which occurs in the output I provided below. My displayAll() function doesn't output anything at all, and displayOne() function is outputting that all my records don't exist when it's only supposed to be one (1008).

My Updated Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iomanip>
using namespace std;

struct ShipRecord
{
    int serialNum;
    int shipType;
    string name;
    int year;
    int cap;
    ShipRecord * link;
};

const int SIZE = 10;
class HashMgr
{
    ShipRecord * hashTable[SIZE] = {nullptr};

public:
    HashMgr()
    {
        string line;
        ifstream inputFile;
        inputFile.open("shipRecords.txt");

        if(inputFile.is_open())
        {
            while (!inputFile.eof())
            {
                getline(inputFile, line);
                addInfo(line);
            }
            inputFile.close();
        }
    }

    ~HashMgr()
    {
        /// To DO: Add your code here to ensure no memory leak occurs
        for (int i = 0; i < SIZE; ++i)
        {
            while (hashTable[i] != nullptr)
            {
                ShipRecord * tempRecord = hashTable[i];
                hashTable[i] = tempRecord->link;
                delete tempRecord;
            }
        }
    }

    HashFunc(int serialNum)
    {
        return serialNum % SIZE;
    }

    void addInfo(string line)
    {
        vector <string> tokens;
        stringstream check1(line);
        string inter;

        cout << endl << "PARSING STRING:\"" << line << "\"" << endl;
        cout << "---------------" << endl;

        while(getline(check1, inter, '"'))
        {
            tokens.push_back(inter);
        }

        for(unsigned int i = 0; i < tokens.size(); i++)
            cout << tokens[i] << endl;

        cout << "---------------" << endl;
    }

    void displayOne(int serialNum)
    {
        int BUCKET = HashFunc(serialNum);
        ShipRecord * tempRecord = hashTable[BUCKET];

        while(tempRecord != nullptr && tempRecord->serialNum != serialNum)
        {
            tempRecord = tempRecord->link;
        }
        if(tempRecord == nullptr)
        {
            cout << serialNum << " <- This ship record does not exist." << endl;
        }
        else if(tempRecord->serialNum == serialNum)
        {
            cout << tempRecord->serialNum << setw(10)
                 << tempRecord->shipType << setw(10)
                 << tempRecord->name << setw(10)
                 << tempRecord->year << setw(10)
                 << tempRecord->cap << setw(10) << endl;
        }
    }

    void displayAll()
    {
        for(int i = 0; i < SIZE; i++)
        {
            ShipRecord * tempRecord = hashTable[i];

            while(tempRecord != nullptr)
            {
                cout << tempRecord->serialNum << setw(10)
                     << tempRecord->shipType << setw(10)
                     << tempRecord->name << setw(10)
                     << tempRecord->year << setw(10)
                     << tempRecord->cap << setw(10) << endl;
                     tempRecord = tempRecord->link;
            }
        }
    }

    void deleteOne(int serialNum)
    {
        cout << "Ship record " << serialNum << " deleted!" << endl;

        int BUCKET = HashFunc(serialNum);
        ShipRecord * tempRecord = hashTable[BUCKET];
        ShipRecord * link;

        while(tempRecord != nullptr && tempRecord->serialNum != serialNum)
        {
            link = tempRecord->link;
            free(tempRecord);
            tempRecord = link;
        }
    }
};

int main()
{
    HashMgr hm;

    cout << "displayAll()" << endl << endl;
    hm.displayAll();

    cout << "displayOne()" << endl << endl;
    hm.displayOne(1009);
    hm.displayOne(1010);
    hm.displayOne(1019);
    hm.displayOne(1029);
    hm.displayOne(1039);
    hm.displayOne(1014);
    hm.displayOne(1008); /// Prompt a message to that the record does not exist

    hm.deleteOne(1009);
    hm.deleteOne(1039);

    cout << "displayAll()" << endl << endl;
    hm.displayAll();

    return 0;
}


My Current Output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

PARSING STRING:"  1009 1 "Royal Queen"     2015 160"
***********
  1009 1
Royal Queen
     2015 160
***********

PARSING STRING:" 1010   2  "Carnival"        2016  1600"
***********
 1010   2
Carnival
        2016  1600
***********

PARSING STRING:" 1019  1  "Ocean King"       2013  110"
***********
 1019  1
Ocean King
       2013  110
***********

PARSING STRING:" 1029 2 "Royal Prince"     2012 2000"
***********
 1029 2
Royal Prince
     2012 2000
***********

PARSING STRING:" 1039 2 "Royal Princess"  2010 2100"
***********
 1039 2
Royal Princess
  2010 2100
***********

PARSING STRING:" 1014 2 "Royal Caribbean" 2016 1600"
***********
 1014 2
Royal Caribbean
 2016 1600
***********
displayAll()

displayOne()

1009 <- This ship record does not exist.
1010 <- This ship record does not exist.
1019 <- This ship record does not exist.
1029 <- This ship record does not exist.
1039 <- This ship record does not exist.
1014 <- This ship record does not exist.
1008 <- This ship record does not exist.
Ship record 1009 deleted!
Ship record 1039 deleted!
displayAll()

Last edited on
¿where do you add something to the table?
your ‛addinfo()' is doing nothing, you parse the line and then just discard the extracted fields
Garbage in. Garbage out. The first thing you need to do is create functions to read and write a ship record. Then change main() temporarily to simply read the input and print it out. So add these to class ShipRecord:
void read(istream &is);
void write(ostream &os);

and put this at the beginning of main():
1
2
3
4
5
    ShipRecord sr;
    while (sr.read(cin), cin) {
        sr.write(cout);
    }
    return 0;


You will have to run the program with "myprog < shipRecord.txt" to get shipRecord.txt attached to cin. Or you could change the code to read from shipRecord.txt, like you do in the HashMgr constructor.

Once you can read and write the records, you can use those methods in the rest of the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    HashMgr()
    {
        string line;
        ifstream inputFile;
        inputFile.open("shipRecords.txt");

        if(inputFile.is_open())
        {
            ShipRecord sr;
            while (sr.read(inputFile), inputFile)
            {
                addInfo(sr);
            }
            inputFile.close();
        }
    }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    void displayOne(int serialNum)
    {
        int BUCKET = HashFunc(serialNum);
        ShipRecord * tempRecord = hashTable[BUCKET];

        while(tempRecord != nullptr && tempRecord->serialNum != serialNum)
        {
            tempRecord = tempRecord->link;
        }
        if(tempRecord == nullptr)
        {
            cout << serialNum << " <- This ship record does not exist." << endl\
;
        }
        else
        {
            tempRecord->write(cout);
        }


    void displayAll()
    {
        cout << "displayAll()\n";
        for(int i = 0; i < SIZE; i++)
        {
            if (hashTable[i]) {
                cout << "Bucket #" << i << '\n';
                // Note the "for" loop below
                for (ShipRecord * tempRecord = hashTable[i];
                     tempRecord;
                     tempRecord = tempRecord->link) {

                    tempRecord->write(cout);
                }
            }
        }
    }


deleteOne() doesn't work at all. Review how to delete an item from a linked list.

Okay, so I updated my code again by making changes to my addInfo() function which was supposed to do three things I forgot to mention:

1. Parse the string into tokens,
2. Add the token values to an object, and
3. Link the object to the HASH table

However, it's giving me this error message saying:

1
2
3
4
5
6
7
8
9

PARSING STRING:"  1009 1 "Royal Queen"     2015 160"
***********
  1009 1
Royal Queen
     2015 160
***********
terminate called after throwing an instance of 'std::invalid_argument'
  what():  stoi


What does this mean and how do I fix this?

My Updated Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iomanip>
using namespace std;

struct ShipRecord
{
    int serialNum;
    int shipType;
    string name;
    int year;
    int cap;
    ShipRecord* link;
};

const int SIZE = 10;
class HashMgr
{
    ShipRecord* hashTable[SIZE] = {nullptr};

public:
    HashMgr()
    {
        string line;
        ifstream inputFile;
        inputFile.open("shipRecords.txt");

        if(inputFile.is_open())
        {
            while (!inputFile.eof())
            {
                getline(inputFile, line);
                addInfo(line);
            }
            inputFile.close();
        }
    }

    ~HashMgr()
    {
        /// To DO: Add your code here to ensure no memory leak occurs
        for (int i = 0; i < SIZE; ++i)
        {
            while (hashTable[i] != nullptr)
            {
                ShipRecord* tempRecord = hashTable[i];
                hashTable[i] = tempRecord->link;
                delete tempRecord;
            }
        }
    }

    HashFunc(int serialNum)
    {
        return serialNum % SIZE;
    }

    void addInfo(string line)
    {
        // Parsing string into tokens.
        vector<string> tokens;
        stringstream check1(line);
        string inter;

        cout << endl << "PARSING STRING:\"" << line << "\"" << endl;
        cout << "---------------"<< endl;

        while(getline(check1, inter, '\"'))
        {
            tokens.push_back(inter);
        }

        for(unsigned int i = 0; i < tokens.size(); i++)
            cout << tokens[i] << endl;

        cout << "---------------"<< endl;

        // Adding token values to an object. ERROR OCCURS HERE
        ShipRecord* sr = new ShipRecord;
        sr->serialNum = stoi(tokens[0]);
        sr->shipType = stoi(tokens[1]);
        sr->name = tokens[2];
        sr->year = stoi(tokens[3]);
        sr->cap = stoi(tokens[4]);

        // Linking the object to the HASH table.
        int bucket = sr->serialNum % SIZE;
        if (hashTable[bucket] == nullptr)
        {
            hashTable[bucket] = sr;
        }
        else
        {
            ShipRecord* tempRecord = hashTable[bucket];

            while (tempRecord->link != nullptr)
            {
                tempRecord = tempRecord->link;
                tempRecord->link = sr;
            }
        }
    }

    void displayOne(int serialNum)
    {
        int bucket = HashFunc(serialNum);
        ShipRecord* tempRecord = hashTable[bucket];

        while(tempRecord != nullptr && tempRecord->serialNum != serialNum)
        {
            tempRecord = tempRecord->link;
        }
        if(tempRecord == nullptr)
        {
            cout << serialNum << " <- This ship record does not exist." << endl;
        }
        else if(tempRecord->serialNum == serialNum)
        {
            cout << tempRecord->serialNum << setw(10)
                 << tempRecord->shipType << setw(10)
                 << tempRecord->name << setw(10)
                 << tempRecord->year << setw(10)
                 << tempRecord->cap << setw(10) << endl;
        }
    }

    void displayAll()
    {
        for(int i = 0; i < SIZE; i++)
        {
            ShipRecord* tempRecord = hashTable[i];

            while(tempRecord != nullptr)
            {
                cout << tempRecord->serialNum << setw(10)
                     << tempRecord->shipType << setw(10)
                     << tempRecord->name << setw(10)
                     << tempRecord->year << setw(10)
                     << tempRecord->cap << setw(10) << endl;
                     tempRecord = tempRecord->link;
            }
        }
    }

    void deleteOne(int serialNum)
    {
        cout << "Ship record " << serialNum << " deleted!" << endl;

        int bucket = HashFunc(serialNum);
        ShipRecord* tempRecord = hashTable[bucket];
        ShipRecord* link;

        while(tempRecord != nullptr && tempRecord->serialNum != serialNum)
        {
            link = tempRecord->link;
            free(tempRecord);
            tempRecord = link;
        }
    }
};

int main()
{
    HashMgr hm;

    cout << "displayAll()" << endl << endl;
    hm.displayAll();

    cout << "displayOne()" << endl << endl;
    hm.displayOne(1009);
    hm.displayOne(1010);
    hm.displayOne(1019);
    hm.displayOne(1029);
    hm.displayOne(1039);
    hm.displayOne(1014);
    hm.displayOne(1008); /// Prompt a message to that the record does not exist

    hm.deleteOne(1009);
    hm.deleteOne(1039);

    cout << "displayAll()" << endl << endl;
    hm.displayAll();

    return 0;
}
$ gdb ./a.out
(gdb) run

PARSING STRING:"  1009 1 "Royal Queen"     2015 160"
---------------
  1009 1 
Royal Queen
     2015 160
---------------

Program received signal SIGABRT, Aborted.
0x00007ffff7a8df25 in raise () from /usr/lib/libc.so.6
(gdb) backtrace
#0  0x00007ffff7a8df25 in raise () from /usr/lib/libc.so.6
#1  0x00007ffff7a77897 in abort () from /usr/lib/libc.so.6
...
#9  0x0000555555587f68 in HashMgr::addInfo (this=0x7fffffffdf18, 
    line="  1009 1 \"Royal Queen\"     2015 160") at foo.cpp:84
#10 0x0000555555584d69 in HashMgr::HashMgr (this=0x7fffffffdf18) at foo.cpp:36
#11 0x00005555555841e0 in main () at foo.cpp:167
(gdb) frame 9
#9  0x0000555555587f68 in HashMgr::addInfo (this=0x7fffffffdf18, 
    line="  1009 1 \"Royal Queen\"     2015 160") at foo.cpp:84
84	        sr->shipType = stoi(tokens[1]);
(gdb) print tokens[1]
$1 = "Royal Queen"
(gdb) quit
your parsing is incorrect and you end up passing a string instead of a number
your tokens are
1
2
3
  1009 1
"Royal Queen"
     2015 160
only three, instead of five
At line 83 you call stoi(), but you're passing it a string that starts with a space. stoi() doens't skip spaces. (edit: see ne555's comment below)

You're making this more complicated than it needs to be. Read the items directly from inputFile using >> and getline()
Last edited on
https://en.cppreference.com/w/cpp/string/basic_string/stol
Discards any whitespace characters (as identified by calling isspace()) until the first non-whitespace character is found
I changed my code in the addInfo() function and made a separate vector function that parses the string and the rest of the tasks does its job in the addInfo() function. However, I'm getting an unusual output where it doesn't display all my ship records in displayAll() function and three of my ship records are couldn't be found in my displayOne() function when it isn't suppose to do that.

Updated Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iomanip>
using namespace std;

struct ShipRecord
{
    int serialNum;
    int shipType;
    string name;
    int year;
    int cap;
    ShipRecord* link = nullptr;
};

const int SIZE = 10;
class HashMgr
{
    ShipRecord* hashTable[SIZE] = {nullptr};

public:
    HashMgr()
    {
        string line;
        ifstream inputFile;
        inputFile.open("shipRecords.txt");

        if(inputFile.is_open())
        {
            while (!inputFile.eof())
            {
                getline(inputFile, line);
                addInfo(line);
            }
            inputFile.close();
        }
    }

    ~HashMgr()
    {
        /// To DO: Add your code here to ensure no memory leak occurs
        for (int i = 0; i < SIZE; ++i)
        {
            while (hashTable[i] != nullptr)
            {
                ShipRecord* tempRecord = hashTable[i];
                hashTable[i] = tempRecord->link;
                delete tempRecord;
            }
        }
    }

    HashFunc(int serialNum)
    {
        return serialNum % SIZE;
    }

    vector<string> parseString(string line)
    {
        vector<string> fields;
        size_t pos = 0;
        string temp = line;

        cout << "PARSING STRING: " << line << endl << endl;

        while(temp.length() > 1)
        {
            if (temp[0] == ' ') // extra white space between tokens
            {
                temp = temp.substr(1);
            }
            else if (temp[0] == '"') // case of double quoted enclosed token
            {
                pos = temp.find_first_of('\"', 1);
                fields.push_back(temp.substr(1, pos - 1));
                temp = temp.substr(pos + 1);
            }
            else // case of space separated token
            {
                pos = temp.find_first_of(' ');
                if (pos != string::npos)
                {
                    fields.push_back(temp.substr(0, pos));
                }
                else
                {
                    fields.push_back(temp.substr(0, temp.length() - 1));
                    break;
                }

                temp = temp.substr(pos);
            }
        }

        return fields;
    }

    void addInfo(string line)
    {
        vector<string> fields = parseString(line);

        ShipRecord* sr = new ShipRecord;
        sr->serialNum = stoi(fields[0]);
        sr->shipType = stoi(fields[1]);
        sr->name = fields[2];
        sr->year = stoi(fields[3]);
        sr->cap = stoi(fields[4]);

        int bucket = sr->serialNum % SIZE;
        if(hashTable[bucket] == nullptr)
        {
            hashTable[bucket] = sr;
        }
        else
        {
            ShipRecord* tempRecord = hashTable[bucket];

            while(tempRecord->link != nullptr)
            {
                tempRecord = tempRecord->link;
                tempRecord->link = sr;
            }
        }
    }

    void displayOne(int serialNum)
    {
        int bucket = HashFunc(serialNum);
        ShipRecord* tempRecord = hashTable[bucket];

        while(tempRecord != nullptr && tempRecord->serialNum != serialNum)
        {
            tempRecord = tempRecord->link;
        }
        if(tempRecord == nullptr)
        {
            cout << serialNum << " <- This ship record does not exist." << endl;
        }
        else if(tempRecord->serialNum == serialNum)
        {
            cout << tempRecord->serialNum << "\t"
                 << tempRecord->shipType << "\t"
                 << tempRecord->name << "\t"
                 << tempRecord->year << "\t"
                 << tempRecord->cap << "\t" << endl;
        }
    }

    void displayAll()
    {
        for(int i = 0; i < SIZE; i++)
        {
            ShipRecord* tempRecord = hashTable[i];

            while(tempRecord != nullptr)
            {
                cout << tempRecord->serialNum << "\t"
                     << tempRecord->shipType << "\t"
                     << tempRecord->name << "\t"
                     << tempRecord->year << "\t"
                     << tempRecord->cap << "\t" << endl;
                     tempRecord = tempRecord->link;
            }
        }
    }

    void deleteOne(int serialNum)
    {
        //cout << "Ship record " << serialNum << " deleted!" << endl;

        int bucket = HashFunc(serialNum);
        ShipRecord* tempRecord = hashTable[bucket];
        ShipRecord* link;

        while(tempRecord != nullptr && tempRecord->serialNum != serialNum)
        {
            link = tempRecord->link;
            free(tempRecord);
            tempRecord = link;
        }
    }
};

int main()
{
    HashMgr hm;

    cout << "displayAll()" << endl << endl;
    hm.displayAll();

    cout << "displayOne()" << endl << endl;
    hm.displayOne(1009);
    hm.displayOne(1010);
    hm.displayOne(1019);
    hm.displayOne(1029);
    hm.displayOne(1039);
    hm.displayOne(1014);
    hm.displayOne(1008); /// Prompt a message to that the record does not exist

    hm.deleteOne(1009);
    hm.deleteOne(1039);

    cout << "displayAll()" << endl << endl;
    hm.displayAll();

    return 0;
}


Updated Output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
PARSING STRING:   1009 1 "Royal Queen"     2015 160

PARSING STRING:  1010   2  "Carnival"        2016  1600

PARSING STRING:  1019  1  "Ocean King"       2013  110

PARSING STRING:  1029 2 "Royal Prince"     2012 2000

PARSING STRING:  1039 2 "Royal Princess"  2010 2100

PARSING STRING:  1014 2 "Royal Caribbean" 2016 1600

displayAll()

1010    2       Carnival        2016    160
1014    2       Royal Caribbean 2016    160
1009    1       Royal Queen     2015    16
displayOne()

1009    1       Royal Queen     2015    16
1010    2       Carnival        2016    160
1019 <- This ship record does not exist.
1029 <- This ship record does not exist.
1039 <- This ship record does not exist.
1014    2       Royal Caribbean 2016    160
1008 <- This ship record does not exist.
displayAll()

1010    2       Carnival        2016    160
1014    2       Royal Caribbean 2016    160
1009    1       Royal Queen     2015    16
You have steadfastly avoided the advice I gave you on November 27 - more than a week ago. Please re-read my post and follow the advice there. You are STILL not reading the records correctly, so it's STILL garbage in - garbage out. There is no point trying to debug the hash code until you're reading the data correctly.
I also think you need to decide what you want to do.
1
2
3
4
5
6
7
8
9
10
11
struct ShipRecord
{
    //
    ShipRecord* link = nullptr;
}:

class HashMgr
{
    ShipRecord* hashTable[SIZE] = {nullptr};
    //
};



Do you want to use a singly linked list or a (C-style) array?
You create dynamic nodes as in a linked list, but you want them to have a ‘position’ as in an array.

If you want to use an array, your code becomes pretty simple. It could be a good start.
Later you could decide to make your container (HashMgr) circular, but not by means of that “HashFunc()” function – that’s definitely wrong.
(I can give you an example code if you want it and this is not an assignment.)

But also if you decide to begin by a linked list you don’t need any hash (why should you? Data are ‘connected’ by pointers).


- - -
About reading from a file… You are really overthinking it.
What about this logic (beware: untested!):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    std::ifstream fin("myfile");
    if ( !fin ) {
        std::cerr << "Cannot open 'myfile'.\n";
        return; // or throw
    }

    // Ok, 'fin' is correctly opened. Let's read it line by line:
    for ( std::string line; std::getline(fin, line); /**/) {
        // At every iteration, 'line' is filled with a new text line.
        // Now let's initialize another stream with that text:
        std::istringstream iss { line };
        // Now 'iss' contains the characters from 'line', but it is a C++ stream,
        // like 'fin' above or std::cin, and you can use it the same way:
        ShipRecord sr;  // or ShipRecord* sr { new ShipRecord };
                        // if you really want to use pointers and linked lists
        
        iss >> sr.serielNum
            >> sr.shipType
            >> sr.name;

        // Here we have a problem: sr.name could comprise two words.
        // But, if it ends with a ", it means it was made by a single word!
        if ( sr.name.back() != '"' ) {  // Alarm! We need to read another string!
            std::string temp;
            iss >> temp;
            sr.name += ' ' + temp;
        }

        iss >> sr.year
            >> sr.cap;

        // 'sr' is ready: add it to your array (C-style array, std::array or
        // std::vector).
    }

I also think you need to decide what you want to do.
He's building a hash table where collisions are resolved with a linked list. So the data structure is fine.

You don't even need the string stream:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    while (true) {
        ShipRecord sr;
        char quote;
        cin >> sr.serialNum >> sr.shipType >> quote;
        getline(cin, sr.name, '"');

        cin >> sr.year >> sr.cap;
        if (!(cin && quote == '"')) break;

        cout << sr.serialNum << "\n"
             << sr.shipType << "\n"
             << '\"' << sr.name << '\"' << "\n"
             << sr.year << "\n"
             << sr.cap << "\n" << endl;
        cin.ignore(1000000000, '\n');
    }



Hi dhayden, thank you for your clarification.
I’ve spent some time writing an (apparently) working version of the OP’s code, so as to practice with these (terribly boring) hash tables. When the OP has posted his code, I’ll offer mine, in the hope to get some feedback.
Topic archived. No new replies allowed.