0

EDITED AT BOTTOM

If you're wondering about how to do this read the accepted answer, it works perfectly

Okay so I've been trying to figure this out for a couple of days now, I've read a bunch of peoples answers but for some reason I keep getting a compiling error when I try to delete duplicates in my program below. Is there a special way that I need to delete these duplicates because of how I set up the vector? Please help, I'm getting extremely frustrated that I can't figure this out.

//libraries
#include <iostream>
#include <string>
#include <set>
#include <fstream>
#include <vector>
#include <list>
#include <algorithm>

//class
class Name_Sorter
{
private:
    //none
public:
    //Name_Sorter();
    //~Name_Sorter();
    std::string name;
    void get_Names(std::string person_Name ){ name = person_Name; }
    void output_Names();

};

//get the user file
std::string get_File()//get input file
{
    std::ifstream fin;
    std::string file_To_Open;

    std::cout << "What is the name of the file where you have stored the names? ";
    getline(std::cin, file_To_Open);

    //std::cout << file_To_Open; // for testing

    return file_To_Open;
}
//output
void Name_Sorter::output_Names()
{
    std::cout << "Name: " << name << std::endl;
}

//sort
bool comp(const Name_Sorter &t1, const Name_Sorter &t2)  //definition
{
    return t1.name < t2.name;
}//compare function

//main program
int main(int argc, const char * argv[])
{
    //variables and vector
    std::vector<Name_Sorter> info;
    std::string names;
    std::string file_To_Open;
    std::ifstream fin;
    int nameCounter = 0;

    Name_Sorter *name_Data;


    //get the file
    file_To_Open = get_File();
    fin.open(file_To_Open.c_str());
    if (!fin.good()) throw "I/O Error";

    //get name
    while(!fin.eof())
    {
        fin >> names;
        fin.ignore(1000, 10);

        name_Data = new Name_Sorter;
        name_Data -> get_Names(names);
        info.push_back(*name_Data);
        delete name_Data;//MM
        nameCounter++;
    }//get names

    fin.close();

    //sorting through the vector by name
    std::sort(info.begin(), info.end(), comp);

    //delete duplicates ****Seems to be a problem here****
    info.erase(std::unique(info.begin(), info.end()), info.end());

    std::vector<Name_Sorter>::iterator iter;

    //transverse vector for output
    for ( iter = info.begin(); iter != info.end(); ++iter)
    {
        /*for(int i = 0; i < nameCounter; i++)
        {
           erase(info.begin(), info.end(), info.end())
        }*/
        iter -> output_Names();
    }//output


    return 0;
}//main

And heres the error message:

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/algorithm:658:97: Invalid operands to binary expression ('const Name_Sorter' and 'const Name_Sorter')

and where the error message links to:

template <class _T1>
struct __equal_to<_T1, _T1>
{
    _LIBCPP_INLINE_VISIBILITY bool operator()(const _T1& __x, const _T1& __y) const {return __x == __y;}
};

Okay so I'm no longer getting the error message, however when I add in the operator== as suggested, the function seems to delete ALL duplicates from the vector, not just all duplicates except for one. If the input is "Hunter, Hunter, Hunter, Toby, Diane, Kiera" I wanted it to output "Diane, Hunter, Kiera, Toby" and for now it will just output "Diane, Kiera, Toby"

bool operator== (const Name_Sorter &t1, const Name_Sorter &t2)
{
    return t1.name < t2.name;
}

Hopefully this can eventually help a lot more people trying to learn how to do this rather than just me.

  • `std::unique` calls `operator==` to compare elements by default. you need to define it for _T1& – Vinay Shukla Apr 22 '15 at 10:03
  • Please see TartanLlama's updated answer. Your `operator==` should compare `t1.name == t2.name` to implement equality. – dyp Apr 22 '15 at 10:08
  • Your `operator==` does this: `t1.name < t2.name` - that's not an equality operator. What you've got there is a less-than operator. – Component 10 Apr 22 '15 at 10:11
  • yeah I saw it, thanks guys! I was really confused about this for a few days now. The original assignment was for a class and manually transversing arrays which I could do just fine but I always want to learn more so I decided to try and use a vector on my own. It works perfectly now even if I don't understand exactly why -- I'm going to read up on operators and their functionality tomorrow / for the next few months. – Hunter Durnford Apr 22 '15 at 10:20

6 Answers6

2

std::unique uses operator== by default. You aren't passing a comparison function as you are in your call to std::sort.

Either define a comparator and fix call to std::unique, like this:

bool eqComp (const Name_Sorter &t1, const Name_Sorter &t2)
{
    return t1.name == t2.name;
}

info.erase(std::unique(info.begin(), info.end(), eqComp), info.end());
//                            include comparator ^^^^                

Or, even better, just overload operator== for your type:

bool operator== (const Name_Sorter &t1, const Name_Sorter &t2)
{
    return t1.name == t2.name;
}

info.erase(std::unique(info.begin(), info.end()), info.end());

Similarly, you can overload operator< to make your std::sort call simpler:

bool operator< (const Name_Sorter &t1, const Name_Sorter &t2)
{
    return t1.name < t2.name;
}

std::sort(info.begin(), info.end());
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
1

A quick suggestion based on what your code appears to do:

  1. Get rid of Name_Sorter - it adds no value - replace it with simply with std::string.
  2. Stop using std::vector and use std::set instead. It will automatically sort and weed out duplicates as each item is appended to it. There appears to be no reason you can't use it and it gets rid of all the de-duping / sorting code.
Component 10
  • 10,247
  • 7
  • 47
  • 64
  • well this was originally a problem for my class but using arrays and manually transversing them which was easy -- I wanted to try and use vectors on my own just to learn more about them. I'll go read about set now though, thanks for the information, everything helps. – Hunter Durnford Apr 22 '15 at 10:14
0

The best way to do so is by using vector, sort + unique as far as performance is concerned.

sort( vec.begin(), vec.end() );
vec.erase( unique( vec.begin(), vec.end() ), vec.end() );

Many other ways of doing the same can be found here

Community
  • 1
  • 1
Vinay Shukla
  • 1,818
  • 13
  • 41
  • OP knows how to do that, see the code. Please take a look at the error message. – dyp Apr 22 '15 at 09:55
0

In Name_Sorter you need to define:

bool operator==(const Name_Sorter& a)
Andrey Lyubimov
  • 663
  • 6
  • 22
0

In my opinion , several things are strange :

  • why calling a function get_Name if this function is setting the name !
  • No constructor for a class ?
  • The way of filling your vector : you don't need pointer to do that
  • You don't need to sort your vector before erasing elements

Appart from that, since the vector is comped of custom elements, you need either to define the ==operand for your class or specifiying a third argument in the function unique which is your comp function. It's the same here --> std::unique and removing duplicates from a container of objects

I quickly rewrite some stuff for your code , and I don't get errors anymore. I guess it's not perfect but at least better

//class
class Name_Sorter
{
private:
    //none
public:
    Name_Sorter();
    //~Name_Sorter();
    std::string name;
    void set_Names(std::string person_Name ){ name = person_Name; }
    std::string get_Names(){ return name; }
    void output_Names();

};

Name_Sorter::Name_Sorter() : name("")
{}

//get the user file
std::string get_File()//get input file
{
    std::ifstream fin;
    std::string file_To_Open;

    std::cout << "What is the name of the file where you have stored the names? ";
    getline(std::cin, file_To_Open);

    //std::cout << file_To_Open; // for testing

    return file_To_Open;
}
//output
void Name_Sorter::output_Names()
{
    std::cout << "Name: " << name << std::endl;
}

//sort
bool comp(const Name_Sorter t1, const Name_Sorter t2)  //definition
{
    return t1.name == t2.name;
}//compare function

//main program
int main(int argc, const char * argv[])
{
    //variables and vector
    std::vector<Name_Sorter> info;
    std::string names;
    std::string file_To_Open;
    std::ifstream fin;
    int nameCounter = 0;


    //get name
    for(int i = 0 ; i < 5 ; i++)
    {    
    Name_Sorter name_Data;
        name_Data.set_Names("lolz");
        info.push_back(name_Data);
        nameCounter++;
    }//get names

    info.erase(std::unique(info.begin(), info.end() , comp) , info.end()) ;

    for(auto i : info){
        cout << i.get_Names() << endl;
    }

    return 0;
}//main
Community
  • 1
  • 1
  • The `std::unique` algorithm removes *consecutive duplicates*, so you need to sort the vector beforehand to make it remove *all duplicates*. – dyp Apr 22 '15 at 10:45
-1

std::unique requires operator== for compared types. Provide such an operator for your class, and it should work

meddle0106
  • 1,292
  • 1
  • 11
  • 22