Kreuzprodukt (Matrix?) eines mehrdimensionalen Arrays

aGeNET

Erfahrenes Mitglied
Moin Moin,

mit mehrdimensionalen Arrays allgemein stehe ich immer etwas auf Kriegsfuß und brauche bei der folgenden Sache etwas Hilfe. Das Endresultat soll so wenig wie möglich Ressourcen brauchen. Ausgangslage ist ein Array mit Produkten - jedes Produkt (P) kann mehrere Optionen (O) und die Optionen wiederum mehrere Optionswerte (V) haben (n >= 1):
Code:
Array (
	[$Pn] => Array (
		[$On] => Array (
			[$Vn] => Array(...)
		)
	)
)
Hier noch ein Beispiel mit Daten zur besseren Veranschaulichung (die Keys sind immer die IDs [Produkt-ID, Options-ID, Optionswert-ID]):
Code:
Array (
	[8] => Array (
			[1] => Array (
					[2] => Array (
							[model] =>
							[price] =>
							[name] => Farbe
							[value] => Pink
						)
					[1] => Array (
							[model] =>
							[price] =>
							[name] => Farbe
							[value] => Schwarz
						)
				)
			[2] => Array (
					[3] => Array (
							[model] => plwf01-p2
							[price] =>
							[name] => Fassungsvermögen
							[value] => 2 Liter
						)
					[4] => Array (
							[model] => plwf01-p4
							[price] =>
							[name] => Fassungsvermögen
							[value] => 4 Liter
						)
				)
		)
	[21] => Array (
			[1] => Array (
					[7] => Array (
							[model] =>
							[price] =>
							[name] => Farbe
							[value] => Hellblau
						)
					[8] => Array (
							[model] =>
							[price] =>
							[name] => Farbe
							[value] => Rosa
						)
				)
			[4] => Array (
					[9] => Array (
							[model] => bsae01-hb62
							[price] =>
							[name] => Größe
							[value] => 62
						)
					[10] => Array (
							[model] => bsae01-hb68
							[price] =>
							[name] => Größe
							[value] => 68
						)
					[11] => Array (
							[model] => bsae01-hb74
							[price] =>
							[name] => Größe
							[value] => 74
						)
					[12] => Array (
							[model] => bsae01-hb80
							[price] =>
							[name] => Größe
							[value] => 80
						)
				)
		)
)

Ich benötige jetzt eine Möglichkeit um aus den gegebenen Daten ein neues Array mit allen möglichen Optionskombinationen zu erstellen, zb:

für Produkt 8:
Farbe/Fassungsvermögen: Pink/2 Liter
Farbe/Fassungsvermögen: Pink/4 Liter
Farbe/Fassungsvermögen: Schwarz/2 Liter
Farbe/Fassungsvermögen: Schwarz/4 Liter

für Produkt 21:
Farbe/Größe: Hellblau/62
Farbe/Größe: Hellblau/68
Farbe/Größe: Hellblau/74
Farbe/Größe: Hellblau/80
Farbe/Größe: Rosa/62
Farbe/Größe: Rosa/68
Farbe/Größe: Rosa/74
Farbe/Größe: Rosa/80

Idealerweise sollte dabei folgendes herauskommen:
Code:
Array (
	[8] => Array (
		[0] => Array ('option_name' => 'Farbe/Fassungsvermögen', 'option_value' => 'Pink/2 Liter')
		[1] => Array ('option_name' => 'Farbe/Fassungsvermögen', 'option_value' => 'Pink/4 Liter')
		[2] => Array ('option_name' => 'Farbe/Fassungsvermögen', 'option_value' => 'Schwarz/2 Liter')
		[3] => Array ('option_name' => 'Farbe/Fassungsvermögen', 'option_value' => 'Schwarz/4 Liter')
	[21] => Array(
		[0] => Array ('option_name' => 'Farbe/Größe', 'option_value' => 'Hellblau/62')
		[1] => Array ('option_name' => 'Farbe/Größe', 'option_value' => 'Hellblau/68')
		[2] => Array ('option_name' => 'Farbe/Größe', 'option_value' => 'Hellblau/74')
		[3] => Array ('option_name' => 'Farbe/Größe', 'option_value' => 'Hellblau/80')
		[4] => Array ('option_name' => 'Farbe/Größe', 'option_value' => 'Rosa/62')
		[5] => Array ('option_name' => 'Farbe/Größe', 'option_value' => 'Rosa/68')
		[6] => Array ('option_name' => 'Farbe/Größe', 'option_value' => 'Rosa/74')
		[7] => Array ('option_name' => 'Farbe/Größe', 'option_value' => 'Rosa/80')
	)
)

MfG aGeNET
 
Zuletzt bearbeitet:
Und wie soll dieses neue Array aussehen?
Willst du es nur für ein Produkt oder für mehrere Produkte die Kombinationen ermitteln?

Gruß, Kalito
 
array_combine könnte sein was du suchst:
http://www.php.net/manual/de/function.array-combine.php


Eventuell musst du vorher deine Arrays je nach Bedarf sortieren.
 
Naja, bei deinem gewünschten Array-Aufbau ist es aber schwer automatisch zu ermitteln, wie der Wert bei option_name sein soll. Ansonsten wirst du nicht um foreachschleifen herumkommen.
 
PHP:
//$array ist dein Array
$result = array();
foreach($array as $groupKey => $group){
    //beide Arrays zurücksetzen
    $partNames = array();
    $partValues = array();
    //Namen und Values extrahieren
    foreach($group as $partKey => $part){
        $partNames = $partNames + array_extract_sub_item($part, 'name');
        $partValues[] = array_extract_sub_item($part, 'value');
    }
    //Namen zusammensetzen
    $names = implode('/', array_unique($partNames));

    //Die beiden Value-Gruppen extrahieren    
    list($part1, $part2) =$partValues;
    //und in einer doppelten Schleife abarbeiten
    foreach($part1 as $item1){
        foreach($part2 as $item2){
            //Den resultat-Array zusammensetzen
            $result[$groupKey][] = array('option_name' => $names, 'option_value' => "{$item1}/{$item2}");
        }
    }
}

print_r($result);



/**
 * Extrahieren ein Subitem aus einem mehrstufigen Array. Der Schlüssel wird beibehalten
 * @param   Array<Key => Array<Node>>   Ein mehrstufiger Array
 * @param   String                      Schlüssel des zu extrahierenden Item
 * @return  Array<Key => Node>
 */
function array_extract_sub_item($array, $key){
    foreach($array as $index => $item) $retArray[$index] = $item[$key];
    return $retArray;
}
Ausgabe
Code:
Array
(
    [8] => Array
        (
            [0] => Array
                (
                    [option_name] => Farbe/Fassungsvermögen
                    [option_value] => Pink/2 Liter
                )
            [1] => Array
                (
                    [option_name] => Farbe/Fassungsvermögen
                    [option_value] => Pink/4 Liter
                )
            [2] => Array
                (
                    [option_name] => Farbe/Fassungsvermögen
                    [option_value] => Schwarz/2 Liter
                )
            [3] => Array
                (
                    [option_name] => Farbe/Fassungsvermögen
                    [option_value] => Schwarz/4 Liter
                )
        )
    [21] => Array
        (
            [0] => Array
                (
                    [option_name] => Farbe/Größe
                    [option_value] => Hellblau/62
                )
            [1] => Array
                (
                    [option_name] => Farbe/Größe
                    [option_value] => Hellblau/68
                )
            [2] => Array
                (
                    [option_name] => Farbe/Größe
                    [option_value] => Hellblau/74
                )
            [3] => Array
                (
                    [option_name] => Farbe/Größe
                    [option_value] => Hellblau/80
                )
            [4] => Array
                (
                    [option_name] => Farbe/Größe
                    [option_value] => Rosa/62
                )
            [5] => Array
                (
                    [option_name] => Farbe/Größe
                    [option_value] => Rosa/68
                )
            [6] => Array
                (
                    [option_name] => Farbe/Größe
                    [option_value] => Rosa/74
                )
            [7] => Array
                (
                    [option_name] => Farbe/Größe
                    [option_value] => Rosa/80
                )
        )
)
 
Danke Yaslaw,

das hilft mir schon mal weiter, allerdings geht dein Vorschlag von zwei Optionen aus - es können jedoch auch mehr sein.
PHP:
list($part1, $part2) = $partValues;
Ich habe mir jetzt noch eine Variante mit $part3 geschrieben, aber das ist zu unflexibel, da die Optionsanzahl nach oben offen ist.
Wie müsste eine (rekursive) Funktion aussehen, die mit count($partValues) arbeiten und dementsprechend $result nach dem vorgegebenen Muster erstellen kann?

MfG aGeNET
 
Kannst du das Testarray mal als var_export anstatt dump zur Verfügung stellen? Sonst ist das testen immer so aufwändig...
 
Kurz googeln -> und hier wurde ich fündig
http://www.php-resource.de/forum/ph...tesisches-produkt-zwischen-arrays-bildet.html
Hab die 2 Funktionen nur noch um eine Variable für das Trennzeichen ergänzt

PHP:
//Trennzeichen definieren
define('C_DELEMITER', '#');

$result = array();
foreach($array as $groupKey => $group){
    //beide Arrays zurücksetzen
    $partNames = array();
    $partValues = array();
    //Namen und Values extrahieren
    foreach($group as $partKey => $part){
        $partNames = $partNames + array_extract_sub_item($part, 'name');
        //Die Values auslesen
        //Da cartesian() mit Index und nixht mit Keys arbeitet, gleich die Keys mit array_values entfernen
        $partValues[] = array_values(array_extract_sub_item($part, 'value'));
    }
    //Namen zusammensetzen. Mit array_unique() verhindern dass ein Name mehrfach vorkommt
    $names = implode(C_DELEMITER, array_unique($partNames));

    //Das Kartesische Produkt erstellen und die Arrays abfüllen
    foreach(cartesian($partValues, C_DELEMITER) as $items){
        $result[$groupKey][] = array('option_name' => $names, 'option_value' => $items);
    }
}

print_r($result);
    

//http://www.php-resource.de/forum/php-developer-forum/79158-funktion-funktion-die-ein-kartesisches-produkt-zwischen-arrays-bildet.html
//Ergänzt um $delemiter
function cartesian_helper($sofar,$arr,$pos,$max,&$collector, $delemiter){
    for($i = 0; $i < count($arr[$pos]);$i++){
        if($pos == $max)
            $collector[] =  trim($sofar.$delemiter.$arr[$pos][$i], $delemiter);
        else
            cartesian_helper($sofar.$delemiter.$arr[$pos][$i],$arr,$pos+1,$max,$collector, $delemiter);
    }
}
//http://www.php-resource.de/forum/php-developer-forum/79158-funktion-funktion-die-ein-kartesisches-produkt-zwischen-arrays-bildet.html
//Ergänzt um $delemiter
function cartesian($arr, $delemiter){
    $bucket = array();
    cartesian_helper('',$arr,0,count($arr)-1,$bucket, $delemiter);
    return $bucket;
}

/**
 * Extrahieren ein Subitem aus einem mehrstufigen Array. Der Schlüssel wird beibehalten
 * @param   Array<Key => Array<Node>>   Ein mehrstufiger Array
 * @param   String                      Schlüssel des zu extrahierenden Item
 * @return  Array<Key => Node>
 */
function array_extract_sub_item($array, $key){
    foreach($array as $index => $item) $retArray[$index] = $item[$key];
    return $retArray;
}

Ausgabe
Code:
Array
(
    [8] => Array
        (
            [0] => Array
                (
                    [option_name] => Farbe#Fassungsvermögen
                    [option_value] => Pink#2 Liter
                )

            [1] => Array
                (
                    [option_name] => Farbe#Fassungsvermögen
                    [option_value] => Pink#4 Liter
                )

            [2] => Array
                (
                    [option_name] => Farbe#Fassungsvermögen
                    [option_value] => Schwarz#2 Liter
                )

            [3] => Array
                (
                    [option_name] => Farbe#Fassungsvermögen
                    [option_value] => Schwarz#4 Liter
                )

        )

    [21] => Array
        (
            [0] => Array
                (
                    [option_name] => Farbe#Größe
                    [option_value] => Hellblau#62
                )

            [1] => Array
                (
                    [option_name] => Farbe#Größe
                    [option_value] => Hellblau#68
                )

            [2] => Array
                (
                    [option_name] => Farbe#Größe
                    [option_value] => Hellblau#74
                )

            [3] => Array
                (
                    [option_name] => Farbe#Größe
                    [option_value] => Hellblau#80
                )

            [4] => Array
                (
                    [option_name] => Farbe#Größe
                    [option_value] => Rosa#62
                )

            [5] => Array
                (
                    [option_name] => Farbe#Größe
                    [option_value] => Rosa#68
                )

            [6] => Array
                (
                    [option_name] => Farbe#Größe
                    [option_value] => Rosa#74
                )

            [7] => Array
                (
                    [option_name] => Farbe#Größe
                    [option_value] => Rosa#80
                )

        )

    [35] => Array
        (
            [0] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder-Optik#Ohne Aufbau/Montage#Bordeaux
                )

            [1] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder-Optik#Ohne Aufbau/Montage#Cafe
                )

            [2] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder-Optik#Ohne Aufbau/Montage#Weiß
                )

            [3] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder-Optik#Ohne Aufbau/Montage#Schwarz
                )

            [4] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder-Optik#Mit Aufbau/Montage#Bordeaux
                )

            [5] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder-Optik#Mit Aufbau/Montage#Cafe
                )

            [6] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder-Optik#Mit Aufbau/Montage#Weiß
                )

            [7] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder-Optik#Mit Aufbau/Montage#Schwarz
                )

            [8] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder#Ohne Aufbau/Montage#Bordeaux
                )

            [9] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder#Ohne Aufbau/Montage#Cafe
                )

            [10] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder#Ohne Aufbau/Montage#Weiß
                )

            [11] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder#Ohne Aufbau/Montage#Schwarz
                )

            [12] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder#Mit Aufbau/Montage#Bordeaux
                )

            [13] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder#Mit Aufbau/Montage#Cafe
                )

            [14] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder#Mit Aufbau/Montage#Weiß
                )

            [15] => Array
                (
                    [option_name] => Bezug#Lieferung#Farbe
                    [option_value] => Leder#Mit Aufbau/Montage#Schwarz
                )

        )

)
 

Neue Beiträge

Zurück