0

I have some code: I need to delete duplicates, I don't really know which step im going wrong at. kinda been losing sleep over this. i feel like im needlessly overcomplicating this but too brain dead atm to see where i went wrong.

STILL HAVING ISSUES =(

I'm making the changes I incoorporated based on suggestions... still running into out of bounds even though my size for the arr is 8... I've tried printing values, it seems to break unexpectedly on the 3rd element but I dont know what to do. any other help would be appreciated...

template <typename T>
void removeDup(std::vector<T> & v)
{
int last = v.size()-1;
for(int i = 0; i <= v.size(); i++)
    {
            if (count(v, i, last, v[i]) > 1){ // if there is more than 1
            v.erase(v.begin()+(i)); // erase it
            }
        }
}


template <typename T>
int count(const std::vector<T> & v, int first, int last, const T& target){

    int index = first;
    int count = 0;

for (index; index < last; index++){
if (v[index] == target){
    count++;
    }
}

return count;
}

template <typename T>
int seqVectSearch(const std::vector<T> & v, int first, int last, const T& target){

    int index = first;
    int returnVal = -1;

for (index; index < last; index++){
if (v[index] == target){
    returnVal = index;
    }
}

return returnVal;
}

template <typename T>
void writeVector(const std::vector<T> & v){
    int i;
    int n = v.size();

    for (i = 0; i < n; i++)
        std::cout << v[i] << ' ';
        std::cout << std::endl;
}

template void removeDup(std::vector<int> & v);
template int seqVectSearch(const std::vector<int> & v, int first, int last, const int& target);
template void writeVector(const std::vector<int> & v);
template int count(const std::vector<int> & v, int first, int last, const int& target);

Output:

Testing removeDup
Original vector is  1 7 2 7 9 1 2 8 9
Vector with duplicates removed is  1 2 7 9 1 2 8 9

1 2 7 9 1 2 8 9
Press any key to continue . . .
aaabbbaaa
  • 15
  • 5
  • If you can sort your vector `std::unique` may help. – Jarod42 Oct 13 '15 at 09:06
  • 1
    If it's okay to modify the order in the vector you could [sort](http://en.cppreference.com/w/cpp/algorithm/sort) it, and then [remove consecutive duplicates](http://en.cppreference.com/w/cpp/algorithm/unique). – Some programmer dude Oct 13 '15 at 09:07
  • 1
    What's your question? – Petr Oct 13 '15 at 09:08
  • `count(v, i, last, i)` should probablly be `count(v, i, last, v[i])`. And you have also to fix iterator after the erase. – Jarod42 Oct 13 '15 at 09:09
  • @molbdnilo I appreciate the reference, but I'm looking to stick with this for now. It's for school and I need to avoid using a bunch of other libraries to help me. I'm mainly just looking for what's wrong in my code logically. – aaabbbaaa Oct 13 '15 at 09:09
  • @Jarod42 what should the iterator be? – aaabbbaaa Oct 13 '15 at 09:11
  • 1
    @aaabbbaaa: You should not do `++i` when you erase element. – Jarod42 Oct 13 '15 at 09:17
  • @JoachimPileborg i'm still having issues... can you help – aaabbbaaa Oct 13 '15 at 10:05
  • For a start, fix your indentation. This makes the code more aesthetically pleasing, easier for others to understand, and easier for _you_ to understand. (I'm personally not even going to look any further at your code until it's indented properly, and I'm sure others take a similar stance). – davmac Oct 13 '15 at 10:17
  • @davmac il try to fix my identation, ive never been good at it.. hold on – aaabbbaaa Oct 13 '15 at 10:19
  • If you want to be real fancy, you could do [this](http://coliru.stacked-crooked.com/a/2a631788d30725d6) – sp2danny Oct 19 '15 at 02:37

3 Answers3

1

If you remove an item, you need to test the next, which now holds the position you where just testing, so updating the index is not right in this case. erase returns an iterator, that can be handy if you want to iterate and conditionally erase some items.
Also note that count is already defined in <algorithm>, you don't need to define your own.

template <typename T>
void removeDup(std::vector<T>& v)
{
    auto iter = v.begin()
    while( iter != v.end() )
    {
        if( std::count(iter, v.end(), *iter) > 1)
            iter = v.erase(iter);
        else
            ++iter;
    }
}

notice however, that this will shuffle a lot of items around, and that the erase-remove idiom might be better suited.

sp2danny
  • 7,488
  • 3
  • 31
  • 53
  • This is a much more advanced and elegant solution =) I just wanted to stick to the algorithms errors, but everyone should use the iterator approach. – Renato Oliveira Oct 13 '15 at 10:31
1

Here is a stable solution (relative order of the elements is preserved) using std::remove_if() from C++'s standard template library. As you can see the actual removal of duplicates only takes ~10 lines of actual code; the rest is (sadly) C++ noise around include files and "pretty" printing a container to stdout.

The core idea is to keep a set of elements that have already been seen inside the predicate; the predicate is a functor that returns true if the element's a duplicate (i.e. can be removed) and false otherwise.

Complexity should be O(n) + O(n log n) - each element needs to be checked once and lookup in the set is O(n log n).

#include <set>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

template <typename T>
struct remove_dups_predicate {
    typedef std::set<T> unique_values_t;


    // Return true if we've seen this element before
    // note that set.insert(...) returns pair<iterator,bool>
    // with the bool telling wether or not the element was 
    // succesfully inserted, i.e. it should *not* be removed
    bool operator()(const T& element) {
        return !unique_values_seen.insert(element).second;
    }
    unique_values_t  unique_values_seen;
};

int main( void ) {
    std::vector<int>           vi{ 1, 7, 2, 7, 9, 1, 2, 8, 9};

    std::cout << "Before: ";
    std::copy(vi.begin(), vi.end(), std::ostream_iterator<int>(std::cout, ", "));
    std::cout << std::endl;

    // Remove the duplicates
    std::vector<int>::iterator it = std::remove_if(vi.begin(), vi.end(),
                                                   remove_dups_predicate<int>());

    // print to stdout
    std::cout << "After: ";
    std::copy(vi.begin(), it, std::ostream_iterator<int>(std::cout, ", "));
    std::cout << std::endl;

    return 0;
}

Running this gives as output:

Before: 1, 7, 2, 7, 9, 1, 2, 8, 9,
After: 1, 7, 2, 9, 8,
emvee
  • 4,371
  • 23
  • 23
  • The code is a little complicated but it is very interesting, it use many features of the std libraries. – Renato Oliveira Oct 13 '15 at 11:38
  • Thx. You should definitely look at http://www.cplusplus.com/reference/algorithm/ for more standard algorithms. The only non-standard bit is the `remove_dups_predicate` functor. Read more here about functors: http://stackoverflow.com/questions/356950/c-functors-and-their-uses – emvee Oct 13 '15 at 11:51
  • This does not actually shorten the vector, something the OP seems to want. Neat solution though, +1 – sp2danny Oct 13 '15 at 13:05
  • however, the standard don't seem to guarantee that stateful functors work with algorithms, see http://stackoverflow.com/questions/6112995/stateful-functors-stl-undefined-behaviour – sp2danny Oct 13 '15 at 13:18
  • Darn. I didn't know that. But then I'd make the state a `std::shared_ptr> unique_elements_seen( new std::set() )` (assuming C++11 is available) – emvee Oct 13 '15 at 13:39
-1

There are some error regarding the algorithm. Mainly, range issues.

v.erase(v.begin()+(i-1));

Your i variable starts at 0, so you don't need to remove the element before it. Just remove begin()+i.

count(v, i, last, i)

You have passed for last argument of count(...) the value of size()-1. So your iteration must be until index <= last, instead of just index < last.

Another problem, the fourth parameter isn't i, is v[i]. Your code only compiles because you is testing with a vector of integers, the same type of the index.

It can have other errors, look carefully to the algorithm and index ranges. A good option is a print on each step of the functions.

Edit: Adding my suggested algorithm

This is the code of the two core methods. Replicate the logic if you want something similar in other tasks.

template <typename T>
void removeDup(std::vector<T> & v) {
    for(int i = 0; i <= v.size(); i++)
    {
            if (count(v, i, v[i]) > 1){ // if there is more than 1
                v.erase(v.begin()+i); // erase it
                i--; // Decrement to stay at the same index.
            }
        }
}

template <typename T>
int count(const std::vector<T> & v, int first, const T& target){
    int count = 0;
    for (int index = first; index < v.size(); index++){
        if (v[index] == target){
            count++;
        }
    }
    return count;
}
Renato Oliveira
  • 494
  • 4
  • 10
  • Were those the only errors? =( I tried fixing what you said and now I can't run it because out of bounds – aaabbbaaa Oct 13 '15 at 09:42
  • Change `last` in `count(v, i, last, v[i])` to `v.size()-1`. When you remove elements of the vector, the size changes, and your variable isn't being updated. – Renato Oliveira Oct 13 '15 at 10:12
  • I thought when you told me to change it you meant in my loop, I didn't realize you meant in the function call... thank you... ugh this was giving me such a headache. i havent slept and its 6 am ty – aaabbbaaa Oct 13 '15 at 10:27
  • your `removeDup` seem to have two problems: One, the `<=` means that you will test one past the last. Second, since you update the index even when you erase items, three consecutive items will be reduced to two, not one. – sp2danny Oct 13 '15 at 10:31
  • Thats true, correcting. But see the sp2danny's answer. It is much more recommended. – Renato Oliveira Oct 13 '15 at 10:34
  • I'm sorry but this is a horrible solution. Too convoluted; it's O(n^2) in testing wether a value is there and on top of that v.erase() will make all elements be shifted downwards. From the standard: **Because vectors use an array as their underlying storage, erasing elements in positions other than the vector end causes the container to relocate all the elements after the segment erased to their new positions.** (http://www.cplusplus.com/reference/vector/vector/erase/) – emvee Oct 13 '15 at 11:31
  • That's why a say, look at sp2danny's answer =) But the rearrange would be inevitable in a simple erase solution. The better option is to verify the vector before inserting a repeated value to prevent duplicates. – Renato Oliveira Oct 13 '15 at 11:37