Python Suchen in einem dict/json

maloz

Captain
Registriert
März 2007
Beiträge
3.125
Hi,

ich habe eine Liste mit Usernamen die ich durchiteriere. Ziel soll sein es pro User in einem Dict nach diesem zu suchen, wobei der User mehrfach vorhanden sein kann (in dem Beispiel User1) und die dazugehörigen Werte auszulesen.

Hier das Beispiel-JSON:
JSON:
{
    "results": [
        {
            "common.status": "ACTIVE",
            "user.user_id": "User1",
            "common.model": "iPhone 7"
        },
        {
            "common.status": "ACTIVE",
            "user.user_id": "User2",
            "common.model": "iPhone 8"
        },
        {
            "common.status": "ACTIVE",
            "user.user_id": "User1",
            "common.model": "iPhone 12 mini"
        },
        {
            "common.status": "ACTIVE",
            "user.user_id": "User3",
            "common.model": "iPhone 12"
        }
    ]
}

Wie bekomme ich nun am besten die Wertigkeiten zu user.user_id == 'User1'?
Jedes mal pro neuem User das ganze JSON durch zu loopen stelle ich mir ungeil vor, komme aber auch auf keine andere Lösung.

Ich bräuchte hier mal ein paar Denkanstöße wie das das besser machen kann. :)
 
For Each ist dein Freund. Im Zweifel mehrfach verschachtelt, um die richtigen Werte zu bekommen.

Eine andere Möglichkeit gibt es normalerweise nicht, es sei denn du nutzt eine Sprache/Bibliothek mit fertigen Funktionen, aber auch die werden im Hintergrund nichts anderes machen.
 
Erstelle ein 2. Dict, in welchem du die Sicht umdrehst:
Python:
{
    "User1":[
{
            "common.status": "ACTIVE",
            "user.user_id": "User1",
            "common.model": "iPhone 7"
        },
        {
            "common.status": "ACTIVE",
            "user.user_id": "User1",
            "common.model": "iPhone 12 mini"
        },
    ],
"User2": [],
....
}

Und immer wenn Dict1 geändert wird, änderst du Dict2 mit. Dies braucht extra Speicherplatz, aber deine Laufzeit ist O(1) für Suchen. Wenn sich allerdings das Dictionary oft ändert, dann fährst du vermutlich mit "im Original suchen" besser. Aber das kommt auf deinen Anwendungsfall an. Die Einträge im 2. Dict kannst du als Referenzen auf die Dicts im Original anlegen und brauchst nicht mal echte Kopien. (z.B. { "common.status": "ACTIVE", "user.user_id": "User1", "common.model": "iPhone 7"})
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: kuddlmuddl
Den User als Key im Dict zu haben wäre natürlich perfekt, leider bekomme ich das Beispiel-JSON aber exakt in diesem Format von einem API Aufruf zurück.

Ich probiere mal ganz stumpf mit for und schaue wie performant das Ganze ist.
Bin natürlich trotzdem noch offen für andere Ideen. :)

edit
Ui. Selbst mit der doppelten for Schleife ist das Script einfach rasend schnell. Ich liebe Python. :D
 
Zuletzt bearbeitet:
maloz schrieb:
Den User als Key im Dict zu haben wäre natürlich perfekt, leider bekomme ich das Beispiel-JSON aber exakt in diesem Format von einem API Aufruf zurück.
Wenns gespeichert wird, könntest aber den Username als key nutzen und hast Zugriff ohne Schleife und kannst ebenso schnell überprüfen, ob der Nutzer da ist.
 
premature optimization is the root of all evil...

Wie @KingLz sagt, musst du abwaegen, wie deine Usage aussieht, ob es sicht lohnt, das in ein dict mit user key umzuwandeln.
Jede Loesung wird aber drueber loopen muessen. Dennoch, wenns kurz und schmerzlos sein soll.

Python:
import json
jdata = """{
    "results": [
        {
            "common.status": "ACTIVE",
            "user.user_id": "User1",
            "common.model": "iPhone 7"
        },
        {
            "common.status": "ACTIVE",
            "user.user_id": "User2",
            "common.model": "iPhone 8"
        },
        {
            "common.status": "ACTIVE",
            "user.user_id": "User1",
            "common.model": "iPhone 12 mini"
        },
        {
            "common.status": "ACTIVE",
            "user.user_id": "User3",
            "common.model": "iPhone 12"
        }
    ]
}"""

#kurzvariante
data = json.loads(jdata)
keys = set([x["user.user_id"] for x in data["results"]])
data_dict = { x : [y for y in data["results"] if x == y["user.user_id"]] for x in keys}
print(data_dict)

#schleifenvariante, aber nur eine loop
data_dict = {}
for x in  data["results"]:
    uid = x["user.user_id"]
    if uid not in data_dict:
        data_dict[uid] = []
    data_dict[uid].append(x)
print(data_dict)
 
Wie gesagt, das json bekomm ich so von der API zurück, wüsste jetzt nicht, wie ich das einfach so speichern kann, dass die Wertigkeiten direkt einem Key mit Usernamen zugeordnet ist (ohne auch wieder loopen zu müssen, was ich vermeiden wollte bzw. mir dann keinen Vorteil bringt).

Das Script läuft jetzt mit doppelter for-Schleife und es ist einfach super schnell. Die notwendigen 3 API Aufrufe dauern am längsten, das eigentliche "doing"/die Logik geht rasend schnell.

Von daher hab ich dann doch gar kein Optimierungspotenzial.
Aber rein aus Interesse hätte es mich interessiert ob man es hätte besser machen können.
 
Verstehe deine Antwort nicht.
Den Code um deine JSON Response in ein dict mit user keys umzuwandeln habe ich dir mit 2 Varianten in meinem obigen Post geliefert.

Rein mathematisch lohnt es sich natuerlich, weil der Zugriff auf das Dict keine loop ist, wenn du nach einem user key suchst.
Aber naja, wenns im Realeinsatz nicht lange dauert, dann ist es ja auch egal.
 
Ich habe den Satz "Wenns gespeichert wird" (der kam nicht von dir ^^) so interpretiert, als könne ich die Antwort vom API Aufruf direkt so abspeichern, dass die User als Key hinterlegt sind. Geht aber nicht.

Man müsste es halt, wie von dir vorgegeben, auch erst umändern. Ich denke das macht erst bei sehr vielen tausend Datensätzen sinn.
 
selbst wenn es eine vorgefertige funktion gäbe, würde die im hintergrund das gleiche machen.
kannste auch als funktion definieren und die api response reinpacken. Aber irgendwas muss ja die Response umändern, es sei denn, du kannst eine andere Form von Response beim Server anfragen.
Das kann dir aber niemand beantworten, wenn man die API nicht kennt.

Edit:
mir ist unklar, was du mit speichern meinst.
Anyhow, so: dann siehst du die response gar nicht in deinem format:
Python:
import json
jdata = """{
    "results": [
        {
            "common.status": "ACTIVE",
            "user.user_id": "User1",
            "common.model": "iPhone 7"
        },
        {
            "common.status": "ACTIVE",
            "user.user_id": "User2",
            "common.model": "iPhone 8"
        },
        {
            "common.status": "ACTIVE",
            "user.user_id": "User1",
            "common.model": "iPhone 12 mini"
        },
        {
            "common.status": "ACTIVE",
            "user.user_id": "User3",
            "common.model": "iPhone 12"
        }
    ]
}"""

def get_response_from_api():
    return json.loads(jdata)

def get_data_transformed(data):
    data_dict = {}
    for x in data["results"]:
        uid = x["user.user_id"]
        if uid not in data_dict:
            data_dict[uid] = []
        data_dict[uid].append(x)
    return data_dict


data = get_data_transformed(get_response_from_api())

print(data['User1'])
 
Zuletzt bearbeitet:
Ich würde auch erstmal simpel anfangen und das Durchiterieren.
Wenn man es CPU intensiv optimieren möchte könnte man die Liste ja dann auch in Chunks aufteilen und mit Threads arbeiten. Oder eben wie schon vorgeschlagen in ein speicherintensiveres O(1) dict.
 
Hi,

also ein Weg die Geschwindigkeit bei der Auswertung bzw. Auch dem parsing von Json und anderen Formaten zu erhöhen ist sie vorher in Objekte zu gießen in etwa so:

https://pythonspeed.com/articles/faster-python-json-parsing/

Sonst wirst du um direktes iterieren nicht herumkommen, es sei denn es ist sortiert oder du sortierst vor dem Auswerten, z.B. Quick oder Mergesort.

Dann könntest du bspw. mit binärer Suche rangehen, musst nur darauf achten, dass erste der Einträge zu erwischen, wenn dein Sortiermerkmal mehr als 1x vorkommen kann :)

Mfg
 
Zuletzt bearbeitet:
Zurück
Oben