Funktion "alle Produktkombinationen" mit dynamischer Anzahl der Eigenschaften

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
tom79
User
Beiträge: 6
Registriert: Montag 4. März 2019, 14:17

Hi Zusammen,
ich grüble schon seit geraumer Zeit an einer Schleife und finde keine Lösung. Vlt hat jemand nicht das Brett vor dem Kopf, wie ich.

Gegeben ist bspw. ein Produkt, bei dem vorher nicht feststeht, wie viele Produkteigenschaften und zugehörige Werte verfügbar sind:

ProduktData = [
["ProduktName1",
["ProduktEigenschaftsName1","Eigenschaft1Wert1", "Eigenschaft1Wert2","Eigenschaft1Wert3"],
["ProduktEigenschaftsName2","Eigenschaft2Wert1", "Eigenschaft2Wert2"],
["ProduktEigenschaftsName3","Eigenschaft3Wert1", "Eigenschaft3Wert2"]
]
]

Return ist eine Liste aller Varianten des Produkts.
Die Funktion soll also anhand eines Zählers erst einmal feststellen, wieviele Schleifen gebildet werden müssen.

Die Funktion soll also dann eine Liste liefern, die so aussieht:
[
["ProduktName1", "EigenschaftsName1", "Eigenschaft1Wert1", "ProduktEigenschaftsName2", "Eigenschaft2Wert1", "ProduktEigenschaftsName3", Eigenschaft3Wert1],
["ProduktName1", "EigenschaftsName1", "Eigenschaft1Wert2", "ProduktEigenschaftsName2", "Eigenschaft2Wert1", "ProduktEigenschaftsName3", Eigenschaft3Wert1],
["ProduktName1", "EigenschaftsName1", "Eigenschaft1Wert3", "ProduktEigenschaftsName2", "Eigenschaft2Wert1", "ProduktEigenschaftsName3", Eigenschaft3Wert1],
["ProduktName1", "EigenschaftsName1", "Eigenschaft1Wert1", "ProduktEigenschaftsName2", "Eigenschaft2Wert2", "ProduktEigenschaftsName3", Eigenschaft3Wert1],
... etc. (also insgesamt in dem Bsp: 3 x 2 x 2 Varianten

Habe schon ein paar Stunden mit itertools und eigenen Schleifen herumprobiert und gesucht und komme aber nicht weiter.
Hilfe wäre nett
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@tom79: wo sollen welche Schleifen gebildet werden? Ich sehe nur Listen.

Listen sind dazu da, gleichartige Objekte zu speichern, also entweder Produkte, oder nur Produktnamen, oder Eigenschaften, aber nicht Produktname, Eigenschaftname und Eigenschaftwert in einer Liste.

Bei solchen verschachtelten Werten ist es dann aber sinnvoller, passendere Datenstrukturen zu verwenden, also neben Listen auch Wörterbucher/Namedtuple/Klassen.
Zum Beispiel mit Wörterbüchern:

Code: Alles auswählen

ProduktData = [
    {
        "name": "ProduktName1",
        "eigenschaften": {
            "Name1": ["Wert1", "Wert2", "Wert3"],
            "Name2": ["Wert1", "Wert2"],
            "Name3": ["Wert1", "Wert2"],
        },
    },
]
Wie willst Du diese Daten jetzt verarbeiten?
tom79
User
Beiträge: 6
Registriert: Montag 4. März 2019, 14:17

quasi eine funktion, die die daten verwendet und davon die liste bildet z.B. print oder einfach return
ArtooDetoo
User
Beiträge: 60
Registriert: Dienstag 4. Dezember 2018, 16:57

Sirius3 hat geschrieben: Montag 4. März 2019, 16:24

Code: Alles auswählen

ProduktData = [
    {
        "name": "ProduktName1",
        "eigenschaften": {
            "Name1": ["Wert1", "Wert2", "Wert3"],
            "Name2": ["Wert1", "Wert2"],
            "Name3": ["Wert1", "Wert2"],
        },
    },
]
Basierend darauf könnte man so etwas machen (geht sicher auch hübscher).

Code: Alles auswählen

list(itertools.product(*list(ProduktData[0]['eigenschaften'].values())))
tom79
User
Beiträge: 6
Registriert: Montag 4. März 2019, 14:17

Toll. Aber wie kriege ich nun noch die Feldnamen in die Liste?

Super! Danke schonmal.
ArtooDetoo
User
Beiträge: 60
Registriert: Dienstag 4. Dezember 2018, 16:57

Code: Alles auswählen

list(ProduktData[0]['eigenschaften'].keys())
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@ArtooDetoo: der `list`-Aufruf ist überflüssig. Und im Beispiel davor der innere `list`-Aufruf.
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sirius3: Kommt das nicht darauf an was man machen will/braucht? `keys()` liefert ja etwas `set`-artiges. Wenn der nächste Verarbeitungsschritt da eine Liste haben will, braucht man `list()`. Wenn's nur irgendwas iterierbares sein muss, dann nicht.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
ArtooDetoo
User
Beiträge: 60
Registriert: Dienstag 4. Dezember 2018, 16:57

Sirius3 hat geschrieben: Montag 4. März 2019, 17:34 @ArtooDetoo: der `list`-Aufruf ist überflüssig. Und im Beispiel davor der innere `list`-Aufruf.
Danke für den Hinweis.
tom79
User
Beiträge: 6
Registriert: Montag 4. März 2019, 14:17

Komme mit den Keys nicht weiter, also den Namen der Eigenschaften.
Der Output sollte lauten:
[
["Produkteigenschaft1":"Wert1.1", "Produkteigenschaft2":"Wert2.1", "Produkteigenschaft3":"Wert3.1"],
["Produkteigenschaft1":"Wert1.2", "Produkteigenschaft2":"Wert2.1", "Produkteigenschaft3":"Wert3.1"],
...
]

Mit keys() erhalte ich zwar alle Labels, aber eben nicht an den zugehörigen Daten.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@tom79: das ist kein gültiges Python. Was hast Du als Input und was versuchst Du hier zu tun? Beschreib doch mal Dein Problem, und nicht, wie Du denkst, dass das Problem zu lösen wäre?
tom79
User
Beiträge: 6
Registriert: Montag 4. März 2019, 14:17

Ok, mag sein, dass ich beim Format etwas geschlampt habe, aber darum gehts ja auch nicht, sondern um die Struktur.
Ob das dann in JSON oder Array rauskommt, ist egal. Mein Ziel ist, dass ich eine Struktur erhalte, die ich anschließend
mit einer Suchfunktion durchsuchen kann.

bsp:
Produkt 1 = Ford Mustang mit Eigenschaften: AnzahlTüren 2, 3, 4, 5 und Eigenschaft2: Farben Rot, Blau und Eigenschaft3 "Reifen": dicke und normale
Ich habe also:

ProduktData = [
{
"Produktname": ["Ford Mustang"],
"Eigenschaften": {
"Türen": [2,3,4,5],
"Farbe": [
"rot",
"blau",
],
"Reifen": [
"Dicke Reifen",
"normale Reifen",
],
},
},
]

Ein anderer Datensatz (das nächste Produkt), könnte eine andere Anzahl von Produkteigenschaften haben.

Nun soll die Funktion alle Möglichen Produktkonfigurationen auswerfen, und zwar unter Nennung des Fieldnames, damit ich später über
den Fieldname eine Abfrage auf die Liste machen kann und dann die besten Matches mit einem Suchstring anzeigen kann.
Meine Ergebnisdatensätze sollen also so aussehen:

[ ("Produktname": "Ford Mustang"), ("Eigenschaften": [("Türen": 2), ("Farbe": "rot "), ("Reifen": "dicke Reifen")]],
[nächste Konfigurationsmöglichkeit],
...
]

Ich kann mit dem Ergebnis der Liste a) eine Abfrage auf den Produktnamen und/oder eine bestimmte Eigenschaft inkl. zugehörigem Wert machen.
Ausgabeformat ist mir egal, weil ich den String ja ggf. noch anpassen kann. Gut natürlich, wenn's gleich passend käme.

Danke für die Hilfe.
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@tom79: Du schlampst bei der Notation der Struktur, wobei Dir die Struktur aber wichtig ist – das widerspricht sich. Man muss halt schon wissen was Du haben willst, und nicht nur so ungefähr, sondern *genau*. Das ist vielleicht auch Dein Problem, denn das Ziel muss man ja für sich selbst erst mal konkret und sauber definieren. Wenn man nur so ungefähr weiss wo man hin will, kann man keinen Code dafür schreiben, denn der hat ja immer ein konkretes Ergebnis.

Was mich hier jetzt verwirrt ist a) das es anscheinend für den Produktnamen auch verschiedene Möglichkeiten geben kann‽ und b) natürlich wieder die Schreibweise des Ergebnisses was weder Python noch JSON ist, man also raten muss was gemeint sein könnte.

Da könnte man doch einfach wieder Wörterbücher nehmen, mit den gleichen Schlüsseln, aber dann halt immer nur einen Wert der ganzen Alternativen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Namen und Werte sollte man mit `zip()` zusammenführen können:

Code: Alles auswählen

#!/usr/bin/env python3
from itertools import product
from pprint import pprint


def main():
    properties = {
        'Farbe': ['rot', 'blau'],
        'Reifen': ['Dicke Reifen', 'normale Reifen'],
        'Türen': [2, 3, 4, 5]
    }
    names = list(properties.keys())
    result = [
        dict(zip(names, values))
        for values in product(*(properties[name] for name in names))
    ]
    pprint(result)


if __name__ == '__main__':
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
tom79
User
Beiträge: 6
Registriert: Montag 4. März 2019, 14:17

Funktioniert super. Herzlichen Dank.
Genauso hab ich's benötigt.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@__blackjack__: jetzt mußte ich doch etwas überlegen, warum `names` eigentlich nötig ist. Aber man sollte ja nicht voraussetzen, dass bei einem sich nicht verändernden Wörterbuch die Schlüssel immer in der gleichen Reihenfolge geliefert werden.
Hätte es dann vielleicht so geschrieben:

Code: Alles auswählen

names, values = zip(*properties.items())
result = [dict(zip(names, v)) for v in product(*values)]
Antworten