Ähnlichen String ausgeben

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Hey,

ich möchte eine Art Telefonbuch erstellen.

Wenn ich ein Teil vom Namen schreibe, soll er mir alle ähnlichen Namen ausgeben.
Bsp.:

Code: Alles auswählen

contacts = {"Jens Follmann" : 123, "Hans Bauer" : 456, "Karl Heinz" : 789, "Theo Schmidt" : 122, "Tim Schmitt" : 133, "Gustav Weber" : 144}
Wenn ich "Schm" eingebe soll er mir ausgeben:

Theo Schmidt 122
Tim Schmitt 133

oder wenn ich "Ba" eingebe soll er mir:

Hans Bauer 456

ausgeben.

Das ganze soll wenn möglich nur mit Modulen aus der Standartbibliothek umgesetzt werden. Ich habe schon mal bisschen gesucht und difflib gefunden, speziell die Funktion get_close_matches. Allerdings liefert mir das nur ungenaue Ergebnisse.

Mein Code:

Code: Alles auswählen

from difflib import get_close_matches
contacts = {"Jens Follmann" : 123, "Hans Bauer" : 456, "Karl Heinz" : 789, "Theo Schmidt" : 122, "Tim Schmitt" : 133, "Gustav Weber" : 144}
similar_names = get_close_matches(input("Bitte Name eingeben: "), contacts.keys(), n = 10)
for name in similar_names:
    print(f"{name} {contacts[name]}")
Wenn ich jetzt bspw. die Nummer von Theo Schmidt haben möchte und "Schm" eingebe, kommt nichts. Wenn ich "Schmi" eingebe, kommt Tim Schmitt. Wenn ich "Theo" eingebe kommt auch nichts.

Ich hoffe ich habe mein Problem verständlich rüberbracht und wäre für Hilfe dankbar.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Okay, ich glaube ich habe es hinbekommen.

Code: Alles auswählen

contacts = {"Jens Follmann" : 123, "Hans Bauer" : 456, "Karl Heinz" : 789, "Theo Schmidt" : 122, "Tim Schmitt" : 133, "Gustav Weber" : 144}
names = list(contacts.keys())
name_to_search = input("Name: ")

for x in range(0, len(names)):
    if name_to_search in names[x]:
        print(f"{names[x]} {contacts[names[x]]}")
Name: Ha
Hans Bauer 456


Name: F
Jens Follmann 123


Name: Sc
Theo Schmidt 122
Tim Schmitt 133
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn es nicht um Trigramme und Levensthein-Abstände geht (mit denen man mit scm auch Schmitt findet), dann reicht ein simples

if needle in haystack:
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nachtrag: haste ja selbst gefunden. Fein. Jetzt nur noch ganz schnell sich abgewöhnen, auf Listen mit Index zuzugreifen. Das macht man in Python nicht.

Code: Alles auswählen

for name in names: 
      print(name)
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Hm, ich wüsste gerade nicht wie ich das ohne auf den Index zuzugreifen lösen könnte. Kannst du mir bitte ein kurzes Snippet zeigen, indem der Unterschied klar wird? Und warum macht man das eigentlich nicht?

Noch eine kurze Frage:

wie bekomme ich die Ausgabe so formatiert, dass die Zahlen alle untereinander stellen, slo statt

Code: Alles auswählen

Name: e
Jens Follmann 123
Hans Bauer 456
Karl Heinz 789
Theo Schmidt 122
Gustav Weber 144
das so darstellen:

Code: Alles auswählen

Name: e
Jens Follmann  123
Hans Bauer     456
Karl Heinz     789
Theo Schmidt   122
Gustav Weber   144
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe dir doch ein snippet gemacht. Und überall wo du jetzt ein Knödeliges “names[x]” stehen hast, kannst du damit einfach “name” benutzen. Und das ist auch ein Grund, warum das besser ist. Weil es weniger Schreibarbeit ist. Andere sind, dass diese Konstrukt (for ding in dinge) auch funktioniert, wenn man NICHT wahlfrei zugreifen kann. Zb bei Zeilen in Dateien.

Für eine solche tabellarische Ausgabe musst du den längsten Namen bestimmen, und daraus dann das notwendige padding berechnen.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Habe es hinbekommen, danke für die Hilfe.

Code: Alles auswählen

contacts = {"Jens Follmann" : 123, "Hans Bauer" : 456, "Karl Heinz" : 789, "Theo Schmidt" : 122, "Tim Schmitt" : 133, "Gustav Weber" : 144}
names = list(contacts.keys())
name_to_search = input("Name: ")
for name in names:
    if name_to_search in name.lower():
        print(f"{name:<15} {contacts[name]}")
Ausgabe:

Code: Alles auswählen

Name: e
Jens Follmann   123
Hans Bauer      456
Karl Heinz      789
Theo Schmidt    122
Gustav Weber    144
nezzcarth
User
Beiträge: 1760
Registriert: Samstag 16. April 2011, 12:47

Jankie hat geschrieben: Mittwoch 5. Juni 2019, 07:44 Das ganze soll wenn möglich nur mit Modulen aus der Standartbibliothek umgesetzt werden.
Wenn das nur für private Zwecke zum Lernen gedacht ist, ist das okay. An sich gibt es aber deutlich bessere Verfahren, gerade auch zur Namenssuche. Da sollte man dann aber auf externe Bibliotheken zugreifen. (Gerade die von __deets__ erwähnte Levenshteindistanz würde ich nicht selbst implementieren, sondern nach Möglichkeit eine der Implementierungen in C/C++ nehmen, für die einen der verbesserten Algorithmen -- also nicht den üblicherweise erklärten Dynamic Programming Algorithmus .)
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Jankie hat geschrieben: Mittwoch 5. Juni 2019, 08:55

Code: Alles auswählen

print(f"{name:<15} {contacts[name]}")
Hmja. Das ist natuerlich nicht so super, denn wenn ein neuer Name dazu kommt, der laenger ist, ist essig. Da solltest du besser die Laenge dynamisch bestimmen, und dann benutzen. Das wird ein bisschen fummelig, weil man dann entweder einen format-string formatieren muss, oder einfach die Namen selbst padded.
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Jankie: Zwischen Schlüssel und Doppelpunkt wird üblicherweise *kein* Leerzeichen gesetzt.

Bei `names` ist der `list()`-Aufruf überflüssig. `names` an sich ist auch überflüssig, man muss nicht jedes Zwischenergebnis an einen Namen binden. Und wenn man letztlich Schlüssel und Wert braucht, kann man auch gleich über `items()` iterieren.

Die 15, beziehungsweise die konkrete Länge sollte man besser aus den Namen ermitteln, damit das auch noch passt wenn jemand mit einem deutlich längeren Namen in den Kontaktdaten vorkommt.

Ein Wörterbuch ist nicht unbedingt die erforderliche Datenstruktur wenn man über alle Einträge iteriert und nicht über Schlüssel auf Werte zugreifen will.

Das Vor- und Nachname in einer Zeichenkette stecken, macht das übliche sortieren nach Nachnamen schwierig bis unmöglich.

Telefonnummern sind keine Zahlen. Wobei es ja nur „so eine Art Telefonbuch“ sein soll, und Du nichts über die Bedeutung der Nummern gesagt hast, wollte ich das trotzdem mal anmerken.

@__deets__: So fummelig wird das gar nicht, man kann Platzhalter verschachteln.

Code: Alles auswählen

#!/usr/bin/env python3


def main():
    contacts = {
        'Gustav Weber': 144,
        'Hans Bauer': 456,
        'Jens Follmann': 123,
        'Karl Heinz': 789,
        'Theo Schmidt': 122,
        'Tim Schmitt': 133,
    }
    name_to_search = input('Name: ').lower()
    max_name_length = max(map(len, contacts))
    for name, number in contacts.items():
        if name_to_search in name.lower():
            print(f'{name:<{max_name_length}} {number}')


if __name__ == '__main__':
    main()
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Oha, neat.
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

@Jankie: die Variable `names` ist überflüssig, weil die Namen ja schon in contacts stehen. Wenn Du in Kleinbuchstaben vergleichst, solltest Du auch den Suchstring umwandeln. Wenn Du die Telefonnummer benutzen willst, iteriere gleich über Schlüssel und Wert.

Code: Alles auswählen

contacts = {"Jens Follmann" : 123, "Hans Bauer" : 456, "Karl Heinz" : 789, "Theo Schmidt" : 122, "Tim Schmitt" : 133, "Gustav Weber" : 144}
name_to_search = input("Name: ").lower()

for name, number in contacts.items():
    if name_to_search in name.lower():
        print(f"name} {number}")
@__deets__: was soll an Formatstring-Formatierungen fummelig sein?

Code: Alles auswählen

f"{name:<{width}} {number}"
Zudem: alles ist besser als selbst padden.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

@__blackjack__:

Das list() hatte ich noch drin, da ich meine erste Version (zweiter Post) immer nur angepasst habe und dort der list aufruf nötig war um über den Index zuzugreifen.
Der Code wird eigentlich nur von mir genutzt, um unsere interne Telefonliste zu vereinfachen, da wir nur eine unübersichtliche Excel-Liste haben und da das suchen manchmal nervig sein kann. (ja ich weiß mit Strg + F kann man auch in Excel suchen aber so gefällt es mir besser) es sind auch keine "richtigen" Telefonnummern, sondern nur Durchwahlnummern. Und sortiert muss es für meine Verwendung nicht sein.

Das mit der 15 war bisschen schlecht von mir gelöst, stimmt. Auch dir danke für die Hilfe und die "Musterlösung".
Antworten