0

Is it possible to do something like:

string word = "Hello";
word[3] = null;
if(word[3] == null){/.../}

in C++, basically making an array element empty. For example if I wanted to remove the duplicate characters from the array I'd set them to null first and then shifted the array to the left every time I found an array index that contained null.

If this is not possible what's a good way of doing something like this in C++ ?

2
  • Adjacent duplicate characters? Or any duplicates whatsoever? Commented Dec 14, 2011 at 16:58
  • Sorry for not being specific enough, but I only used string as an example. I am wondering how to do something for arrays that contain ints or other general objects. And I'm interested in any duplicates whatsoever. Commented Dec 14, 2011 at 17:09

4 Answers 4

5

If you want to remove adjacent duplicate characters, you can do this:

std::string::iterator new_end = std::unique(word.begin(), word.end());
word.erase(new_end, word.end());

If you want to mark arbitrary characters for removal, you can skip the marking and just provide the appropriate predicate to std::remove_if:

new_end = std::remove_if(word.begin(), word.end(), IsDuplicate);
word.erase(new_end, word.end());

However, I can't think of an appropriate predicate to use here that doesn't exhibit undefined behavior. I would just write my own algorithm:

template<typename IteratorT>
IteratorT RemoveDuplicates(IteratorT first, IteratorT last)
{
    typedef typename std::iterator_traits<IteratorT>::value_type
            ValueT;
    std::map<ValueT, int> counts;
    for (auto scan=first; scan!=last; ++scan)
    {
        ++counts[*scan];
        if(counts[*scan] == 1)
        {
            *first = std::move(*scan);
            ++first;
        }
    }
    return first;
}

Or, if you don't care about the order of the elements, you could simply sort it, then use the first solution.

Sign up to request clarification or add additional context in comments.

3 Comments

That seems like a pretty elegant method. Can you explain what it does?
@user1066113: The first one calls the unique function on the whole range of the string. Which removes all adjacent duplicates, shifting the non-duplicates forward. It returns an iterator to the end of the range after the shifting has taken place. This iterator is then used with the string member function erase to erase the elements starting at that point to the end of the string, effectively resizing it.
@user1066113: The second one does something very similar using the remove_if function. Instead of just adjacent duplicates though, it can remove elements based on any condition you wish, which is provided by the third argument. I just made up the IsDuplicate function.
0

This is possible, since a single element of a string is an element within a char-array and thus representable as pointer, i. e. you can retrieve the address of the element. Therefore you can set word[3] = null. Your if-construct is valid but the compiler prints a warning, this is because NULL is only a pointer constant. Alternatives would be: if (!word[3]) or if(word[3] == 0).

But in any case you should consider using STL algorithms for removing duplicates.

3 Comments

if you don't care about the order of the elements, you can use sort(word.begin(), word.end()); and then use word.erase( unique(word.begin(), word.end()), word.end() );
a single element of a string is a pointer That's... wrong. Also, the if construct is valid, because NULL and chars are both integral types, and compare just fine.
So what would be right then? Regarding the NULL comparison the Compiler throws a warning.
0

I think you should take a look at the algorithm in the STL.
You are not very specific about what you want to remove but maybe this helps:

    std::string string_with_dup("AABBCCDD");
    std::string string_without_dup;
    std::cout << string_with_dup << std::endl;
    // with copy
    std::unique_copy(string_with_dup.begin(), string_with_dup.end(), std::back_inserter(string_without_dup));
    std::cout << string_without_dup << std::endl;
    // or inplace
    string_with_dup.erase(std::unique(string_with_dup.begin(), string_with_dup.end()), string_with_dup.end());
    std::cout << string_with_dup << std::endl;

Comments

0

If you want to remove all duplicates (not only the adjacent ones, you should use the erase-remove idiom with something like this

#include <iostream>
#include <map>
#include <string>
#include <algorithm>

using namespace std;

struct is_repeated {
    is_repeated( map<char,int>& x ) :r(&x) {};
    map<char,int>* r;
    bool operator()( char c ) {
        (*r)[c]++;
        if( (*r)[c] > 1 )
            return true;
        return false;
    }
};

int main (int argc, char**argv)
{
    map<char,int> counter_map;
    string v = "hello hello hello hello hello hello hello";
    cout << v << endl;
    is_repeated counter(counter_map);
    v.erase( remove_if(v.begin(), v.end(), counter ), v.end() );
    cout << v << endl;
}

outputs (as of this):

hello hello hello hello hello hello hello
helo 

2 Comments

Your solution makes certain assumptions about the behavior of remove_if that are not guaranteed, see an example where it fails: ideone.com/rr5YU
Wow, I had no idea that the remove_if would operate in copies of my predicate... can you tell me why? (I'm editing my response to a working solution)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.