searching a vector of objects

Pages: 12
hello devs


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
i have a vector which was populated with information from a text file, 
//text file
10101 Mike Sanderson
10202 Bart Simpson
10105 Lisa Simpson
30304 Homer Simpson


vector <Student> studentlists;// this is a vector of objects i created using a while loop below

//func to populate vector 

void func2 (string filename)
{
    
    ifstream in_file;
    string line;
    string line2;
    int num; 
    string name;
    int regnumber;

    
    in_file.open("../studs.txt");
    while (!in_file.eof()){
        
        in_file >> num >> line >> line2;
        name = line;
        regnumber = num;
        
        Student student1(name,regnumber);
        //system("pause");
        studentlists.push_back(student1);
    }
    in_file.close();
}


i am trying to search the vector for e.g. int 10101 but i have found no solution, im still fairly new to c++ and this is my biggest challenge yet, what is the best approach for me to search the vector of objects and get the index of the object in the vector.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void search(){
    
    int myReg = 10101;
    auto it = find_if(studentlists.begin(), studentlists.end(), [&myReg](const Student& obj) {return obj.getRegNo() == myReg;});
    
    if (it != studentlists.end())
    {

    auto index = std::distance(studentlists.begin(), it);
    cout << "element found" << endl;

    //this is my latest attempt but when i run this i think it 
    //just searches with the address of the int i delcared.
}


Last edited on
While it was a toss up between 'polluted' and 'populated' for a minute or two, I decided to lean towards 'populated'.
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
#include <vector>
#include <algorithm>

struct student
{
    int reg_number() const { return reg_num ; }

    // ...

    int reg_num = 0 ;

    // ...
};

// search student_list for student with reg_num
// return the index of the object [ 0, student_list.size()-1 ] if found;
// return student_list.size() if not found
std::ptrdiff_t search( const std::vector<student>& student_list, int reg_num )
{
    const auto eq_reg_num = [reg_num] ( const student& s ) { return s.reg_number() == reg_num ; };
    const auto iter = std::find_if( student_list.begin(), student_list.end(), eq_reg_num ) ;

    // return index [0,N-1] if found, or N if not found where N == student_list.size()
    return iter - student_list.begin() ; // random access iterator; so supports -
}

// search student_list for student with reg_num
// return the index of the object [ 0, student_list.size()-1 ] if found;
// return student_list.size() if not found
std::size_t search2( const std::vector<student>& student_list, int reg_num )
{
    for( std::size_t i = 0  ; i < student_list.size() ; ++i )
        if( student_list[i].reg_number() == reg_num ) return i ; // if found, return index

    return student_list.size() ; // not found
}
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
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

struct Student
{
   int id;
   string name;
};


vector <Student> studentlists;


void readStudents()
{
   int id;
   string firstname, lastname;
   ifstream in( "students.dat" );
   while ( in >> id >> firstname >> lastname ) studentlists.push_back( { id, firstname + ' ' + lastname } );
}


void writeStudents()
{
   for ( Student &st : studentlists ) cout << st.id << "  " << st.name << '\n';
}


int main()
{
   readStudents();
   writeStudents();

   int id;
   cout << "Enter an ID: ";   cin >> id;
   auto it = find_if( studentlists.begin(), studentlists.end(), [id]( Student st ){ return st.id == id; } );
   if ( it == studentlists.end() ) cout << "Student not found\n";
   else                            cout << "Name: " << it->name;
}
i am trying to search the vector for e.g. int 10101 but i have found no solution, im still fairly new to c++ and this is my biggest challenge yet, what is the best approach for me to search the vector of objects and get the index of the object in the vector

Do you realize that std::find doesn't return an "index", it returns an iterator?

//this is my latest attempt but when i run this i think it
//just searches with the address of the int i delcared.

What make you "just" think this? Have you tried to actually print the "found" element to be sure?

Why all the global variables? You really should learn to pass the required variables to and from your functions using parameters and function return values.

Why all the meaningless variable and function names? Using meaningful names helps to document the code and make it much easier to read.

thank you very much @JLBorges and lastchance, i will attempt these now,


What make you "just" think this? Have you tried to actually print the "found" element to be sure?

Why all the global variables? You really should learn to pass the required variables to and from your functions using parameters and function return values.

Why all the meaningless variable and function names? Using meaningful names helps to document the code and make it much easier to read.


@jlb, like i said earlier, i am still new to c++, im a student at uni so take it easy on me :), im sure one day i will learn and write proficient code.
Last edited on
Instead of while (!in_file.eof()) use while(in_file >> num >> line >> line2)
im sure one day i will learn and write proficient code.

Well today is one day, so perhaps you may want to try to start now.

OK, time’s up. Just green tick the thread and we can all move on.
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
#include <vector>
#include <algorithm>

struct student
{
    int reg_number() const { return reg_num ; }

    // ...

    int reg_num = 0 ;

    // ...
};

// search student_list for student with reg_num
// return the index of the object [ 0, student_list.size()-1 ] if found;
// return student_list.size() if not found
std::ptrdiff_t search( const std::vector<student>& student_list, int reg_num )
{
    const auto eq_reg_num = [reg_num] ( const student& s ) { return s.reg_number() == reg_num ; };
    const auto iter = std::find_if( student_list.begin(), student_list.end(), eq_reg_num ) ;

    // return index [0,N-1] if found, or N if not found where N == student_list.size()
    return iter - student_list.begin() ; // random access iterator; so supports -
}

// search student_list for student with reg_num
// return the index of the object [ 0, student_list.size()-1 ] if found;
// return student_list.size() if not found
std::size_t search2( const std::vector<student>& student_list, int reg_num )
{
    for( std::size_t i = 0  ; i < student_list.size() ; ++i )
        if( student_list[i].reg_number() == reg_num ) return i ; // if found, return index

    return student_list.size() ; // not found
}

i attempeted @JLborges solution, but i couldnt get it to work unfortunately i do not understand this syntax it seems very advanced, how would i implement these in my main, are these functions or how do i call them.


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
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

struct Student
{
   int id;
   string name;
};


vector <Student> studentlists;


void readStudents()
{
   int id;
   string firstname, lastname;
   ifstream in( "students.dat" );
   while ( in >> id >> firstname >> lastname ) studentlists.push_back( { id, firstname + ' ' + lastname } );
}


void writeStudents()
{
   for ( Student &st : studentlists ) cout << st.id << "  " << st.name << '\n';
}


int main()
{
   readStudents();
   writeStudents();

   int id;
   cout << "Enter an ID: ";   cin >> id;
   auto it = find_if( studentlists.begin(), studentlists.end(), [id]( Student st ){ return st.id == id; } );
   if ( it == studentlists.end() ) cout << "Student not found\n";
   else                            cout << "Name: " << it->name;
}




i also attempted @last chance, the issue with that was i got an error "no matching function to "std::vector <Student> ::push_back(<brace-enclosed initializer list>)"


also the data members are private, so this also resulted in a compiler error.


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
/student.cpp
#include <iostream>
#include "Student.h"
#include <string>
#include <map>
using namespace std;


Student::Student(const string &name, int regNo)
    :Person(name)
    , marks()
{
//        this->name = name;
//        this->regNo = regNo;
 
        "student constructor called" << endl;
        cout << "regnumber = " << regNo << endl;
}


int Student::getRegNo() const
{
    cout << "reg = " << regNo << endl;
    return regNo;
}


im sorry to drag this on, thanks in advance, who ever can help

the vector needs to contain objects as later i would be adding values to the maps of each individual object.
Last edited on
Can we see your complete actual code? Especially Student.h, which includes your declaration of a student.
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
#ifndef _STUDENT_H_
#define _STUDENT_H_

#include <string>
#include <map>
#include <stdexcept>
#include "Person.h"

using namespace std;

class NoMarkException: public exception
{
};

class Student: public Person
{   public:
        // constructor should initialise name and registration number using arguments
        // and initialise marks map to be empty
        Student(const string &name, int regNo);

	   // method to return registration number
	   int getRegNo() const;

	   // method to add the mark to the map
	   // if a mark for the module is already present it should be overwritten
	   void addMark(const string& module, float mark);

	   // method to retrieve the mark for a module
	   // should throw NoMarkException if student has no mark for that module
	   float getMark(const string &module) const;

    private:
	   int regNo;
	   map<string, float> marks;  // keys are modules, values are marks in range 0.0 to 100.0

    // friend function to output details of student to stream
    // should output name, regno, and minimum, maximum and average marks on a single line
    // if the student has no marks "has no marks" should be output instead of the marks
    friend ostream& operator<<(ostream &str, const Student &s);
};

#endif 


that is the student.h file
Last edited on
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
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

class Student          // Should simulate your classes vis-a-vis this problem
{
   string name;
   int regNo;

public:
   Student(const string &name, int regNo) : name( name ), regNo( regNo ) {}

   int getRegNo() { return regNo; };
   string getName() { return name; };
};


vector <Student> studentlists;


void readStudents()
{
   int regNo;
   string firstname, lastname;

// ifstream in( "students.dat" );                  // for final use
   istringstream in( " 10101 Mike Sanderson\n"     // for testing in cpp.sh
                     " 10202 Bart Simpson  \n"
                     " 10105 Lisa Simpson  \n"
                     " 30304 Homer Simpson \n" );  

   while ( in >> regNo >> firstname >> lastname ) studentlists.push_back( Student( firstname + ' ' + lastname, regNo ) );
}


void writeStudents()
{
   for ( Student &st : studentlists ) cout << st.getRegNo() << "  " << st.getName() << '\n';
}


int main()
{
   readStudents();
   writeStudents();

   int regNo;
   cout << "Enter a regNo: ";   cin >> regNo;
   auto it = find_if( studentlists.begin(), studentlists.end(), [regNo]( Student st ){ return st.getRegNo() == regNo; } );
   if ( it == studentlists.end() ) cout << "Student not found\n";
   else                            cout << "Name: " << it->getName();
}
Last edited on
thanks lastchance for your help, greatly appreciate it man

the only issue is the writeStudents system outputs 0 for the regnumbers which leads to a student not found out from find if

Last edited on
Hello @blendero777,
I have just edited to use a stringstream to simulate the file input. It should run fine in cpp.sh.

If it fails to write out the students first then it hasn't found the input file. Make sure that that corresponds to yours - including the relative directory. You can put lines in to check that the file opened correctly, but I was trying to avoid complicating the demonstration code.

Your teacher probably wants you to use const more often than I do. Better to do what he/she says.

In cpp.sh:
10101  Mike Sanderson
10202  Bart Simpson
10105  Lisa Simpson
30304  Homer Simpson
Enter a regNo: 10105
Name: Lisa Simpson 
Last edited on
If you plan to search inside your vector many times, you could consider overloading an operator==, so that you can call the std::find() simpler version. I don’t know if this can be of any help:
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
// text file:
// 10101 Mike Sanderson
// 10202 Bart Simpson
// 10105 Lisa Simpson
// 30304 Homer Simpson
#include <algorithm>
#include <fstream>
#include <iostream>
#include <istream>
#include <memory>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>


struct Student {
    int num_reg {};
    std::string name;
    std::string surname;

    Student() = default;
    Student(std::string line);

//friend:
    friend bool operator== (const Student& lhs, const Student& rhs);
    friend bool operator== (const Student& lhs, int num_reg);
    friend std::ostream& operator<< (std::ostream& os, const Student& rhs);
};


Student::Student(std::string line)
{
    std::istringstream iss { line };
    iss >> num_reg >> name >> surname;
}


bool operator== (const Student& lhs, const Student& rhs)
{
    return    std::tie(lhs.num_reg, lhs.name, lhs.surname)
           == std::tie(rhs.num_reg, rhs.name, rhs.surname);
}


bool operator== (const Student& lhs, int num_reg)
{
    return lhs.num_reg == num_reg;
}


std::ostream& operator<< (std::ostream& os, const Student& rhs)
{
    os << rhs.num_reg << ' ' << rhs.name << ' ' << rhs.surname;
    return os;
}


std::unique_ptr<std::basic_istream<char>> getStream(const std::string& fname);
std::vector<Student> populateFromStream(std::basic_istream<char>& bis);
void printVec(const std::vector<Student>& v);


int main()
{
    auto students { populateFromStream( *getStream("test") ) };
    printVec(students);
    auto mike { std::find(students.begin(), students.end(), 10101) };
    if (students.end() != mike) {
        std::cout << "Found:\n" << *mike << '\n';
    }
}


std::unique_ptr<std::basic_istream<char>> getStream(const std::string& fname)
{
    if (fname == "test") {
        return std::make_unique<std::istringstream>(
            "10101 Mike Sanderson\n"
            "10202 Bart Simpson\n"
            "10105 Lisa Simpson\n"
            "30304 Homer Simpson\n"
        );
    } else {
        return std::make_unique<std::ifstream>( fname );
    }
}


std::vector<Student> populateFromStream(std::basic_istream<char>& bis)
{
    std::vector<Student> v;
    for (std::string line; std::getline(bis, line); /**/) {
        Student s(line);
        v.push_back(s);
    }
    return v;
}


void printVec(const std::vector<Student>& v)
{
    std::cout << "Students:\n";
    for (const auto& e: v) {
        std::cout << e << '\n';
    }
    std::cout << '\n';
}


Output:
Students:
10101 Mike Sanderson
10202 Bart Simpson
10105 Lisa Simpson
30304 Homer Simpson

Found:
10101 Mike Sanderson

the file is a text file which i wrote as

ifstream in( "../studs.txt" );

it does write the student names just the reg number thats 0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
0  Mike Sanderson
reg = 0
0  Bart Simpson
reg = 0
0  Lisa Simpson
reg = 0
0  Homer Simpson
reg = 0
0  John Terry
reg = 0
0  Wayne Rooney
reg = 0
0  Cristiano Ronaldo
reg = 0
0  Lionel Messi
reg = 0
0  Tom Cruise
reg = 0
0  Monty Python


this is the command line output
Last edited on
It is useful to have the whole name with a blank in the middle. I guess it didn't like passing by reference when there was no variable associated with the argument.

You could replace line 36 by
1
2
3
4
5
   while ( in >> regNo >> firstname >> lastname ) 
   {
      string name = firstname + ' ' + lastname;
      studentlists.push_back( Student( name, regNo ) );
   }
the file is a text file which i wrote as

ifstream in( "../studs.txt" );

it does write the student names just the reg number thats 0


I used a different filename and location. So you can amend my code to use whatever you want instead. For more flexibility, you could also pass the filename as an argument to readStudents().
1
2
3
4
5
6
7
8
9
10
11
12
13
It is useful to have the whole name with a blank in the middle. I guess it didn't like passing by reference when there was no variable associated with the argument.

You could replace line 36 by
1
2
3
4
5
   while ( in >> regNo >> firstname >> lastname ) 
   {
      string name = firstname + ' ' + lastname;
      studentlists.push_back( Student( name, regNo ) );
   } 


this gave me this output, it appears to be a memory address?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
reg = 9903792
9903792  Mike Sanderson
9903792  Lionel Messieg = 9903792
9903792  Bart Simpson
reg = 9903792
9903792  Lisa Simpson
reg = 9903792
9903792  Homer Simpson
reg = 9903792
9903792  John Terry
reg = 9903792
9903792  Wayne Rooney
reg = 9903792
9903792  Cristiano Ronaldo
reg = 9903792
reg = 9903792
9903792  Tom Cruise
reg = 9903792
9903792  Monty Python


 
If you plan to search inside your vector many times, you could consider overloading an operator==, so that you can call the std::find() simpler version. I don’t know if this can be of any help:


this looks advanced, my knowledge with friend functions and overloading operators isnt good atm, i dont know how i would be able to build on this code further xd
Pages: 12