Hunter Durnford
Hunter Durnford

Reputation: 27

How do you delete duplicate strings/data in a vector?

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.

Upvotes: 0

Views: 418

Answers (6)

Gr&#233;gory Vaumourin
Gr&#233;gory Vaumourin

Reputation: 312

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

Upvotes: 0

Component 10
Component 10

Reputation: 10497

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.

Upvotes: 1

TartanLlama
TartanLlama

Reputation: 65620

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());

Upvotes: 2

meddle0106
meddle0106

Reputation: 1292

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

Upvotes: -1

Andrey Lyubimov
Andrey Lyubimov

Reputation: 673

In Name_Sorter you need to define:

bool operator==(const Name_Sorter& a)

Upvotes: 0

Vinay Shukla
Vinay Shukla

Reputation: 1844

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

Upvotes: 0

Related Questions