Unordered_map - Schlüssel nicht eindeutig

Hi zusammen, wisst ihr, was ich (einfach) machen kann, um in einer unordered_map alle Elemente auszugeben, wenn der Schlüssel nicht eindeutig ist: USA und Canada haben in der Map die gleiche Vorwahl und es wird nur Canada ausgegeben:

vector my_codes {1, 55, 960, 1};
for (int code : my_codes) {
cout << code << ": " << IDD_codes[code] << „\n“;
}

1: Canada
55: Brazil
960: Maldives
1: Canada

Mein Versuch mit 2-maliger Aufführung ist natürlich kläglich gescheitert… :slight_smile:

Hallo,

in einer Map sind die Keys für gewöhnlich eindeutig. So wird das also nicht funktionieren.

Da die ganze Rufnummernzuteilung in Nordamerika ein Chaos ist, müsstest du das anders angehen:

Gruß,
Steve

1 Like

Hallo!

Eine Map kann nur eindeutige Keys haben. Je nach Implementierung führt der Versuch, einen neuen Wert mit bereits enthaltenem Key einzufügen, zu einem Fehler, oder dazu, daß der alte Eintrag überschrieben wird.

Abgesehen davon: Welchen Unterschied erwartest du denn zwischen

IDD_codes[1]

und

IDD_codes[1]

? Denn genau das gibt dein Code ja aus.

Wie @steve_m schon schrieb, Vorwahlen sind eine sehr undankbare Sache. Dafür muß man nicht mal ins Ausland schauen. Unsere Nachbarstadt hat die gleiche Vorwahl wie wir, mit ner 9 dahinter. Folglich gibt es in unserer Stadt keine Nummer, die mit einer 9 beginnt. Man könnte argumentieren, dass wir eine gemeinsame Vorwahl haben, und die nächste(n) Ziffer(n) dann den Bezirk angeben, so wie es früher auch innerhalb einer Stadt der Fall war.
Das stimmt aber nicht, denn dann müsste ich in meiner Stadt eine Nummer der Nachbarstadt ohne Vorwahl erreichen können, und das funktioniert nicht.

1 Like

Nein - in der Map sind schon beide Einträge drin - ich will sie nur rausholen…

Schreibt gerne auch nen Link zum Lesen - meist schreibt man ja hier ehe man richtig nachdenkt. :slight_smile:

Muss heute abend hier mal was lesen:
https://de.cppreference.com/w/cpp/container/unordered_map

Wie @sweber schon schrieb: Du kannst den Wert nicht zweimal hineinschreiben. Zumindest nicht mit den Methoden aus Deinem Link (Modifiers). Bei ::insert z.B. steht:

Fügt Element(e) in den Container ein, falls noch kein Element mit gleichem Schlüssel im Container existiert.

1 Like

Hi!

Nur zur Info:

Folgendes sollte eine vollständige Liste aller Key-Value-Paare der Map ausgeben:

for (auto e : IDD_codes) {
    cout << "IDD_codes[" << e.first << "] = " << e.second << endl;
}

Spätesten daran wirst du sehen, dass ein erster Wert mit dem Key 1 nicht mehr existiert. und durch den zweiten überschrieben wurde.

1 Like

Ah ja: Es ist so dass nur der 1. in der initialisierten map drin ist - 2 weitere gleiche keys danach!! werden dann nicht mehr gespeichert.

Die folgende (von mir interpretierte ursprünglich englische - das nur als Info wegen dem denglish) Aufgabe:
Handle nun, dass USA UND Canada beide den gleichen Country Code haben! Wie lösbar?

wäre dann am einfachsten gelöst, wenn 1=USA&Canada in einem Eintrag erscheint, gell?
Oder habt ihr andere sinnvollere Lösungen dafür?
Sprich: Wenn ich eigene maps mit uneindeutigem Schlüssel erstelle, ist ja trotzdem das Dilemma der Uneindeutigkeit für eine Auswertung doof…

Hier ist der Code:

Das wäre aber ja dann eher keine „richtige“ Programmieraufgabe…??? :slight_smile:

Hi!

Es muss zuerst klar sein, wie die Aufgabe gelöst werden soll, danach erst wird es zu einer Programmieraufgabe.

Wie du richtig erkennst: Die Vorwahl +1 gilt für die USA, Kanada und viele der karibischen Inseln. Danach kommt dann das, was bei uns die Städtevorwahl ist, bei denen aber den Staat bezeichnet. Das heißt, für eine Unterscheidung musst du herausfinden, wie sich kanadische und US-amerikanische Nummern denn nun unterscheiden.

Wenn du da eine Übersicht hast, dann kann man über ein Programm nachdenken.

1 Like

Nebenbei nochmal gefragt - ich bin Newby in C++, komme aber von java (hab aber mit maps da auch noch nciht viel gemacht)- finde ich das Verhalten bei der Initialisierung nicht ganz okay. Er sieht ja, dass ich mit meinen 3x Initialisierungen für +001 scheitere (1. USA-hab ich noch hinzugefügt, 2. United States & 3. Canada - war schon dirn im example). Warum sagt er aber der 2. & 3. Belegung nicht bei der Initialisierung, dass er nicht das Member zuordnen konnte, da der key schon belegt ist?
Bitte nicht nur als Begründung nennen: "Da muss halt der Nutzer drauf achten - oder ist gegen Selbstverantwortungs-Pjhilosophie von C++ - das ist mir etwas zu wenig - das denke ich mir so schon…

Von meiner Erwartung von java her, würde ein kleiner Kommentar in der Console (konnte United States & Canada nicht zuordnen…) da schon viel helfen beim Verständnis? Kann ich ja auch selbst machen, wenn ich eine eigene „unordered_map“ mal erstelle. :slight_smile:

Hallo!

Da muss halt der Nutzer drauf achten!

Im Ernst: C ist eine alte, recht archaische Hochsprache, die genau das tut, was du ihr sagst. Schreiben an eine Position eines Arrays, die größer ist als das Array? Kein Problem. Das Betriebssystem wird das meist mit einem Segmentation Fault quittieren, aber C selbst hat damit kein Problem. C++ ist prinzipiell eine Erweiterung von C, aber auch nicht viel anders.

Natürlich hat sich auch C/C++ über die Jahre weiter entwickelt, aber das grundsätzliche Paradigma, dass der Programmierer wissen muss, was er tut, ist erhalten geblieben.

Java ist da weitaus moderner, es erschlägt dich beispielsweise mit Warnungen. Das tut C/C++ prinzipiell nicht.

Jetzt noch ein paar Dinge:

Der Compiler schaut sich deinen Code an, und wird vieles daran optimieren. Unter anderem wird er feststellen, dass da eine Map mit viel Inhalt direkt im Code steht, und wird im fertigen Programm sozusagen einen vorgefertigten Datenblock anlegen, der diese Daten enthält. Wenn du das Programm ausführst, wird da nicht erst ne Map mit all den Daten gefüllt, sondern sie ist quasi schon da. Das heißt, in deinem Fall müsste eine solche Warnung vom Compiler kommen, nicht vom Programm selbst. (Tut sie aber nicht)

Wenn man sich die Doku zu unordered maps anschaut, gibt es prinzipiell zwei Möglichkeiten, Daten einzufügen. mit [] werden Daten hinzugefügt oder ersetzt, mit .insert() werden sie nur eingefügt, wenn der Key noch nicht existiert. Dabei wird ein std::pair zurück gegeben, dessen zweites Element angibt, ob der Wert denn nun eingefügt wurde, oder nicht.

Der unten aufgeführte Code nutzt drei maps. Im ersten Fall so wie bei dir durch Initialisierung gefüllt, im zweiten durch [] , und im dritten durch .insert().

Die Ausgabe sieht so aus:

Original
A[3] = Drei
A[2] = Zwei
A[1] = Eins die Erste


Manuelles Insert per []
B[3] = Drei
B[2] = Zwei
B[1] = Eins die Vierte


Manuelles Insert insert()
Eins die Erste  -> 1
Zwei            -> 1
Drei            -> 1
Eins die Zweite -> 0
Eins die Dritte -> 0
Eins die Vierte -> 0
C[3] = Drei
C[2] = Zwei
C[1] = Eins die Erste

Man sieht also, dass [] ersetzt, .insert() aber nicht, und dabei genau diese Info auch rausgibt. Die Initialisierung hält sich an die .insert()-Methode.

#include <unordered_map>
#include <string>
#include <iostream>
#include <vector>
using std::unordered_map;
using std::string;
using std::cout;
using std::vector;

int main() {
    
    std::cout << "Original"<< std::endl;
    
	unordered_map<int, string> A {
        { 1, "Eins die Erste" },
        { 2, "Zwei" },
        { 3, "Drei" },
        { 1, "Eins die Zweite" },
        { 1, "Eins die Dritte" },
        { 1, "Eins die Vierte" }
    };

	for (auto e : A) {
        std::cout << "A[" << e.first << "] = " << e.second << std::endl;
    }
    //##################################################################
    std::cout << "\n\nManuelles Insert per []"<< std::endl;
    unordered_map<int, string> B;
        
    B[1]="Eins die Erste";
    B[2]="Zwei";
    B[3]="Drei";
    B[1]="Eins die Zweite";
    B[1]="Eins die Dritte";
    B[1]="Eins die Vierte";

	for (auto e : B) {
        std::cout << "B[" << e.first << "] = " << e.second << std::endl;
    }
    
    //##################################################################
    std::cout << "\n\nManuelles Insert insert()"<< std::endl;
    unordered_map<int, string> C;
         
    std::cout << "Eins die Erste  -> " << C.insert({1, "Eins die Erste"}).second  << std::endl;
    std::cout << "Zwei            -> " << C.insert({2, "Zwei"}).second            << std::endl;
    std::cout << "Drei            -> " << C.insert({3, "Drei"}).second            << std::endl;
    std::cout << "Eins die Zweite -> " << C.insert({1, "Eins die Zweite"}).second << std::endl;
    std::cout << "Eins die Dritte -> " << C.insert({1, "Eins die Dritte"}).second << std::endl;
    std::cout << "Eins die Vierte -> " << C.insert({1, "Eins die Vierte"}).second << std::endl;


	for (auto e : C) {
        std::cout << "C[" << e.first << "] = " << e.second << std::endl;
    }
    
}

Zum Schluss noch das: Du siehst, dass du dich bei einer unordered map noch nicht mal drauf verlassen kannst, dass die Reihenfolge, mit der du sie gefüllt hast, erhalten bleibt. Ist halt unordered. Das heißt, wenn bei deinem Vorwahl-Problem zuerst ein Kiterium geprüft werden muss, und dann erst ein anderes, dann lässt sich das damit nicht umsetzen.

Noch was generelles:
Im ersten Moment scheint es ja, dass eine unordered map eine einfachere Version einer ordered map ist, und dass man dann wohl die nimmt, wenn ordered nicht notwendig ist.

Aber…

Eine ordered map ist wie ein Telefonbuch. Ob ein Name darin auftaucht, lässt sich rasend schnell prüfen, und im gleichen Schritt findet man heraus, wo man einen neuen Eintrag einfügen müsste.

Eine unordered map muss dagegen ALLE Einträge durchgehen, um herauszufinden, ob ein Eintrag bereits existiert.

Das heißt: Beim lesenden Zugriff ist die ordered Map schneller, und beim Einfügen… auch.

2 Like

Dank dir @sweber für deine tolle & umfangreiche Antwort!! Ich werde sie ein paar Tage lang lesen um alles zu verstehen was da drin steht und den Code in eclipse laufen lassen um zu lernen!!
C hab ich in den 90gern schon viel mit gemacht (3D-Grafik in Echtzeit zur RoboterbahnDarstellung das alles mit Matrizen aber nur. Die Welt hier fasziniert mich aber auch enorm mit c++ - vielleicht mache ich die letzten 10 Dienstjahre noch einen auf SW-Entwickler… Wer ein skript hierzu haben will zum lernen (m.E. sehr gut und sehr viel Code Übungen zum nachvollziehen ca. 300 S.) - dem gebe ich das gerne (über privat) weiter…

VG!! Dani

Achso noch wichtig zu wissen vllt.: Das Skript enthält keine Grundlagen (GrundDatentypen, Schleifen etc.) sondern eher nur advanced topics in OOP, Memory Management, Threads… ein paar schöne Algorithmen…
und ist von mir nur zum Selbststudium (und Spass) angelegt -> nicht vollständig und korrekt und perfekt…

Dieses Thema wurde automatisch 30 Tage nach der letzten Antwort geschlossen. Es sind keine neuen Nachrichten mehr erlaubt.