Mehrfacheinträge entfernen
-
Gibt es eine Möglichkeit aus einem Array oder Vector Mehrfacheinträge zu entfernen, so dass nur eindeutige Werte vorhanden sind? Für die Sortierung gibts ja sort(). Gibts dafür auch eine Methode oder muss man das anders machen und wie ginge das am besten?
-
Ich kenne jetzt keine für dieses Problem zugeschnittene Funktion.
Die STL-Algorithmen werden dir aber sicher dabei helfen. Du kannst mit
std::count()z.B. die Anzahl Vorkommen zählen und mitstd::vector::erase()dann entsprechende Elemente löschen.
-
Es gibt einen STL-Algorythmus der fast das macht was du möchtest:
std::unique
std::unique"schiebt" aufeinanderfolgende, sich wiederholende Elemente eines Containers hinter das letzte eindeutige Element des Containers. Wenn z.B. ein Vektor die folgenden Elemente hätte:1 2 2 3 3 4Dann würde
std::uniquedie Elemente folgendermaßen "umordnen":1 2 3 4 2 3Aber wie oben erwähnt müssen die sich wiederholenden Elemente aufeinanderfolgend sein, weshalb
std::uniquean einem Vektor mit den folgenden Elementen nichts umsortieren würde:1 2 3 2 3 4Allerdings kannst du ja mit
std::sortdie Elemente des Containers so sortieren, dass diese dann hintereinander stehen würden. Anschließend könntest du dann die sich wiederholenden und nun auch aufeinanderfolgenden Elemente mitstd::uniquean das Ende des Containers "schieben" und diese dann mit Hilfe des zurückgegebenen Iterators, der auf das Element hinter dem letzten eindeutigen Element des Containers zeigt, löschen.Puh, falls das jetzt zu kompliziert war, habe ich hier noch ein kleines Beispiel:
//main.cpp #include <iostream> #include <vector> #include <algorithm> using std::cout; using std::endl; using std::vector; using std::sort; using std::unique; int main() { vector<int> ivec; // Vector mit 2 Werten füllen ivec.push_back(2); ivec.push_back(5); // Füllt den Vector mit weiteren Werten for(int i = 0; i != 10; ++i) ivec.push_back(i); // und zusätzlich noch ein paar bereits enthaltene Werte hinzufügen ivec.push_back(3); ivec.push_back(7); ivec.push_back(9); // Alle Elemente des Vectors ausgeben cout << "Alle Werte des Vectors:" << endl; for(vector<int>::iterator it = ivec.begin(); it != ivec.end(); ++it) cout << *it << " "; cout << endl; // Die Elemente des Vectors sortieren, damit Elemente mit gleichen Werten // hintereinander stehen sort(ivec.begin(), ivec.end()); // 'unique' "schiebt" aufeinanderfolgende, sich wiederholende Elemente hinter // das letzte eindeutige Element des Vectors und gibt einen Iterator auf das // erste nach hinten "geschobene" Element zurück vector<int>::iterator unique_iter = unique(ivec.begin(), ivec.end()); // Und jetzt noch die Elemente, die sich wiederholten, löschen ivec.erase(unique_iter, ivec.end()); // Nun alle noch übrigen, eindeutigen Elemente ausgeben cout << "Eindeutige Elemente:" << endl; for(vector<int>::iterator it = ivec.begin(); it != ivec.end(); ++it) cout << *it << " "; cout << endl; return 0; }Die erste Ausgabe des Beispiels würde lauten:
Alle Werte des Vectors: 2 5 0 1 2 3 4 5 6 7 8 9 3 7 9Und die Ausgabe nach
std::sortundstd::uniqueundstd::vector::erase()würde lauten:Eindeutige Elemente: 0 1 2 3 4 5 6 7 8 9So, das war hoffentlich hilfreich

-
Danke, so richtig blicke ich da noch nicht durch aber mal sehen. Von array_unique() in PHP habe ich mal was gehört. Evtl. kann man sowas nachbauen

-
dixidix schrieb:
Evtl. kann man sowas nachbauen

Wozu nachbauen was es schon gibt? Eine Referenz für std::unique findest du übrigens hier:
http://www.cplusplus.com/reference/algorithm/unique.htmlDas zeigt auch, dass Mizar's Ausführungen nicht ganz richtig waren: die mehrfach vorkommenden Elemente werden NICHT nach hinten kopiert.
Wenn du viele mehrfache Elemente hast ist es unter Umständen auch sinnvoll, den vector anschließend "zu schrumpfen":
std::vector<MyType> vec; //füllen... std::vector<MyType>::iterator begin = vec.begin(), end = vec.end(); std::sort(begin, end); std::vector(begin, std::unique(begin,end)).swap(vec); //temporärer vector wird mit dem ergebnis von unique copy-initialisiert //swap sorgt dafür, dass in vec dieses ergebnis steht und der komplette alte Inhalt im temporären vector //der temporäre vector wird zerstört und nimmt den ganzen "müll" mitDer Vollständigkeit halber für (dynamische) Arrays, inklusive "Schrumpfung":
MyType *myArr = new MyType[wievielauchimmer]; //myArr füllen std::sort(myArr, myArr+wievielauchimmer); std::size_t newlen = std::unique(myArr, myArr+wievielauchimmer) - myArr; MyType *tmp = new MyType[newlen]; std::copy(myArr, myArr+newlen, tmp); delete[] myArr; //alten Müll löschen myArr = tmp; //myArr zeigt jetzt auf das neue Array mit den "einzigartigen" Elementen
-
Wozu nachbauen was es schon gibt?
Danke für den Tipp, soweit bin ich noch nicht vorgedrungen. Werd's mal probieren, sobald ich dazu komme.
-
pumuckl schrieb:
Das zeigt auch, dass Mizar's Ausführungen nicht ganz richtig waren: die mehrfach vorkommenden Elemente werden NICHT nach hinten kopiert.
Das verstehe ich ehrlich gesagt nicht. In der Referenz heißt es doch auch:
Removes the duplicate consecutive elements from the range [first,last). This is done by removing all the elements that compare equal to the element right preceding them (only the first element in each group of consecutive equal elements is kept).
Also, mehrfach vorkommende, aufeinanderfolgende Elemente wandern hinter das letzte eindeutige Element.
Vielleicht hatte ich in meinem Post das ganze auch etwas zu ausufernd geschrieben. Das Prinzip ist eigentlich ganz einfach:
vector<int> ivec; //füllen std::sort(ivec.begin(), ivec.end()) // Elemente mit gleichem Wert stehen hintereinander ivec.erase(std::unique(ivec.begin(), ivec.end()), ivec.end());std::unique "vertauscht" sich gleichende, aufeinanderfolgende Elemente, so dass am Ende sich wiederholende Elemente hinter dem letzten eindeutigen Element stehen. Der zurückgegebene Iterator zeigt auf das Element hinter dem letzten eindeutigen Element und kann dann z. B. dazu benutzt werden, den Anfang eines zu löschenden Bereiches anzugeben.
-
Wie wär's mit std::set ?
-
unique vertauscht nicht, es geht etwas anders vor.
Beispiel:
122345 wird zu 123455Die zweite 2 wird gelöscht indem alle Elemte dahinter (bis auf das letzte) einen "aufrücken".
1222345 wird zu 1234545
Das Ergebnis ist also so als wenn du das eigentliche Ergebnis in seiner neuen Länge über das alte rüberkopierst.
Übrigens geht auch folgendes:
myVec.resize(std::unique(myVec.begin(), myVec.end()) - myVec.begin());
-
Fellhuhn schrieb:
Beispiel:
122345 wird zu 123455Die zweite 2 wird gelöscht indem alle Elemte dahinter (bis auf das letzte) einen "aufrücken".
1222345 wird zu 1234545
Das Ergebnis ist also so als wenn du das eigentliche Ergebnis in seiner neuen Länge über das alte rüberkopierst.
Ok, ich habe deine Beispiele mal schnell getestet und musste dabei feststellen, das ich die Vorgehensweise von std::unique bisher doch etwas falsch verstanden hatte.

Naja, zumindest lag ich damit richtig, dass man std::unique dazu nutzen kann Mehrfacheinträge zu entfernen.
-
Fellhuhn schrieb:
Übrigens geht auch folgendes:
myVec.resize(std::unique(myVec.begin(), myVec.end()) - myVec.begin());Jup. Im Vergleich dazu war mein Vorschlag weniger performant (da das Ergebnis einmal komplett kopiert wird), aber platzsparender (weil der Speicher der überflüssigen Elemente freigegeben wird). Außerdem werden bei dem Kopiervorgang Iteratoren ungültig, beim resize() nicht (aber auch nur, weil der Vector nicht vergrößert wird)
Ist also ein wenig Geschmackssache und ein wenig Profilersache.
-
also so hat es was gebracht:
... sort(v.begin(), v.end()); v.resize(unique(v.begin(), v.end()) - v.begin()); ...
-
pumuckl schrieb:
...aber platzsparender (weil der Speicher der überflüssigen Elemente freigegeben wird)...
Kommt dabei natürlich immer drauf an, was man weiterhin mit dem Vector vor hat. Wenn kurz darauf eh wieder neue Elemente angehängt werden, ist es von Vorteil den Platz nicht freizugeben. Ansonsten natürlich anders herum.