Nicole: Kunterbuntes Array rekursiv reduzieren

Hallo erst mal ;-),
zur Zeit scheine ich einen Blackout zu haben,
komm nicht weiter...

Ich möchte eine kunterbuntes, mehrdimensionales
Array so formatieren, dass am Ende nur noch
ein "sauberes", mehrdimensionales Array vorhanden
ist, dessen Werte nicht leer sind.

Dabei 2 Möglichkeiten in Funktionparameter möglich:
1. Bei allen leeren Werten die keys entfernen
2. Wenn in einer Struktur alle Werte(nur dann)
leer sind, den ersten key(ebene 1) entfernen.

Kunterbuntes Array:
$values[] = "einzelner Wert";
$values[] = "";
$values['alpha'] = array('dyn' =>0, 'stat' =>'hund','xtra' =>2);
$values['beta'] = array('blabla' =>'', 'stat' =>'','xtra' =>'',array('a','b','c'));
$values['gamma'] = array('dyn' =>'katze', 'stat' =>5,'xtra' =>'');
$values[] = "";
$values['delta'] = array('farbe' =>'', 'stat' =>'','xtra' =>'');

--------------------------

Erwünschtes Zielarray 1( also leere Inhalte/Schlüssel entfernen):
$values[] = "einzelner Wert";

$values['alpha'] = array('dyn' =>0, 'stat' =>'hund','xtra' =>2);
#$values['beta'] = array(array('a','b','c')); unnötiges leerees Array
also soll werden zu :

$values['beta'] = array('a','b','c');
$values['gamma'] = array('dyn' =>'katze', 'stat' =>5);

------------------
Erwünschtes Zielarray 2
(Haupstruktur Struktur entfernen, nur wenn "alle" werte leer sind):

$values[] = "einzelner Wert";
$values['alpha'] = array('dyn' =>0, 'stat' =>'hund','xtra' =>2);
$values['beta'] = array('blabla' =>'', 'stat' =>'','xtra' =>'',array('a','b','c'));
$values['gamma'] = array('dyn' =>'katze', 'stat' =>5,'xtra' =>'');

------------------------------------------

Erster Lösungsansatz:

function redu_array($ar,$option = 1)
{
// option 1 = Keys reduzieren
// option 2 = Erste Ebene entfernen

foreach($ar as $k =>$w)
{
if(is_array($w)){redu_array($ar[$k]);}// weitere ebenen suchen
elseif(!$w){
unset($ar[$k]);
unset($w);
}

return $ar;
} // end of func.

------------------------------

Das funktioniert natürlich nicht ganz, aber
ich komme einfach nicht weiter... (seit 2 Tagen ;-)

Weiss jemand Rat oder kennt jemand eine Funktion zu diesem Zweck ?

Gruss und Dank
Nikki

  1. Hello,

    bei Rekursion wird die eigentliche Entscheidung/Manipulation immer in der Entität getroffen/durchgeführt.

    Du müsstest also erstmal definieren, wann ein Element leer ist.
    Wenn es denn leer ist, löschst Du es.
    Diese Prüfung führst Du erst auf dem Rückweg durch, dann werden auch die Löschungen der tieferen Strukturen in den höheren beachtet.

    Harzliche Grüße aus http://www.annerschbarrich.de

    Tom

    --
    Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
    Nur selber lernen macht schlau
  2. Ich bin immer noch nicht davon überzeugt, dass du dir mit dem großen Datenhaufen einen Gefallen tust. Es wird auch nicht besser, wenn du den großen Datenhaufen erst auf Überflüssiges hin auswertest, um dann die den kleineren Datenhaufen nach anderen Kriterien auszuwerten.

    Doch nun zu deiner Funktion. (Ich erlaube mir mal, die etwas lesefreundlicher zu zitieren)

    function redu_array($ar, $option = 1) {
    // option 1 = Keys reduzieren
    // option 2 = Erste Ebene entfernen

    foreach ($ar as $k => $w) {

    if (is_array($w)) {
          redu_array($ar[$k]);

    hier musst du das zurückgegebene reduzierte Array auch wieder dem $ar[$k] zuweisen, sonst war das ja alles für den Stubentiger

    $ar[$k] = redu_array($w);

    }// weitere ebenen suchen
        elseif (!$w) {

    empty($w) statt !$w gäbe hier deutlicher zu verstehen, worauf du hinauswillst

    außerdem wolltest du auch das zurückgebene Array auf "leer" prüfen oder nicht? Also if statt elseif und $ar[$k] statt $w prüfen

    if (empty($ar[$k])) {

    unset($ar[$k]);
          unset($w);

    unset($w) ist überflüssig, da es bei jedem Schleifenwert sowieso neu gefüllt wird. (Du weißt, dass das nur eine Kopie des Wertes aus dem Array ist und keine Referenz darauf?)

    }

    Das ganze if nochmal neu geschrieben sähe dann so aus:
          if (is_array($w))
            $ar[$k] = redu_array($w);

    if (empty($ar[$k]))
            unset($ar[$k]);

    } //foreach   (hattest du vergessen zu schließen)

    return $ar;

    Und dann war da noch der Fall 'beta':
        return (count($ar) == 1) ? reset($ar) : $ar;

    } // end of func.

    Für Option 2 würde ich eine eigene Funktion schreiben, da sich diese Funktionalität m.E. nicht so einfach oben integrieren lässt, ohne zu dem Daten-Kuddelmuddel nicht auch noch ein Code-Kuddelmuddel hinzuzufügen.

    Du kannst ja damit schon mal anfangen, ich tue jetzt erstmal was anderes...

    1. Hallo Dedlfix,
      erst mal VIELEN DANK.
      Deine Modifizierung klappt einwandfrei, wenngleich
      ich mich immer noch schwer tue das ganze bildhaft
      und vor allem chronologisch nachzuvollziehen, aber
      ich komm schon dahinter ;-)

      Ich bin immer noch nicht davon überzeugt, dass du dir mit dem großen Datenhaufen einen Gefallen tust. Es wird auch nicht besser, wenn du den großen Datenhaufen erst auf Überflüssiges hin auswertest, um dann die den kleineren Datenhaufen nach anderen Kriterien auszuwerten.

      Ja, lieber wäre mir auch ich müsste nicht alles
      unnötige (leere Arrays/werte) über den Browser senden.
      Hatte das Problem schon mal hier angefragt und Du hattest
      versucht zu helfen: http://forum.de.selfhtml.org/archiv/2005/2/t101154/

      Aber leider fällt mir keine Alternative ein und so leistet
      mir diese Funktion jetzt doch schon Einiges an Scriptreduzierung.

      Hier nochmal die komplette Funktion inclusive option 2,
      für alle Dies nutzen wollen.
      ------------------------------------------

      function redu_array($ar,$option =1)
      {

      // option 1 = Keys in allen Ebenen reduzieren, wenn kein Wert
      // option 2 = Auf erste Ebene entfernen, wenn kein Wert

      Das erste Array als Kopie für option 2

      if($option == 2){$ar_org = $ar;}

      foreach($ar as $k =>$w)
      {
      if (is_array($w)){$ar[$k] = redu_array($w);}
      if (empty($ar[$k])){unset($ar[$k]);}
      }

      if(count($ar)==1){reset($ar);}

      if($option ==2){
      foreach($ar_org as $k =>$w){if(!$ar[$k]){unset ($ar_org[$k]);}}
      $ar = $ar_org; unset($ar_org);
      }

      return $ar
      } // end of func.

      ---------------------------------------

      Gruss und Dank
      Nikki

      1. Nachtrag: Syntax Error, ich hatte in der Funktion
        das letzte Semikolon vergessen, bitte nicht schimpfen ;-)

        .....
        return $ar;
        } // end of func.

      2. Hello,

        Hallo Dedlfix,
        erst mal VIELEN DANK.
        Deine Modifizierung klappt einwandfrei, wenngleich
        ich mich immer noch schwer tue das ganze bildhaft
        und vor allem chronologisch nachzuvollziehen, aber
        ich komm schon dahinter ;-)

        Ich bin immer noch nicht davon überzeugt, dass du dir mit dem großen Datenhaufen einen Gefallen tust. Es wird auch nicht besser, wenn du den großen Datenhaufen erst auf Überflüssiges hin auswertest, um dann die den kleineren Datenhaufen nach anderen Kriterien auszuwerten.

        Ja, lieber wäre mir auch ich müsste nicht alles
        unnötige (leere Arrays/werte) über den Browser senden.
        Hatte das Problem schon mal hier angefragt und Du hattest
        versucht zu helfen: http://forum.de.selfhtml.org/archiv/2005/2/t101154/

        Aber leider fällt mir keine Alternative ein und so leistet
        mir diese Funktion jetzt doch schon Einiges an Scriptreduzierung.

        Hier nochmal die komplette Funktion inclusive option 2,
        für alle Dies nutzen wollen.

        function redu_array($ar,$option =1)
        {

        // option 1 = Keys in allen Ebenen reduzieren, wenn kein Wert
        // option 2 = Auf erste Ebene entfernen, wenn kein Wert

        Das erste Array als Kopie für option 2

        if($option == 2){$ar_org = $ar;}

        foreach($ar as $k =>$w)
        {

        if (is_array($w)){$ar[$k] = redu_array($w);}  # das ist die Stelle, an der man
            if (empty($ar[$k])){unset($ar[$k]);}          # das Verhalten der Funktion wesentlich
          }                                               # beeinflusst. Die Löschung findet hier erst
                                                          # auf dem Rückweg statt.

        if(count($ar)==1){reset($ar);}

        if($option ==2){
        foreach($ar_org as $k =>$w){if(!$ar[$k]){unset ($ar_org[$k]);}}
        $ar = $ar_org; unset($ar_org);
        }

        return $ar
        } // end of func.


        Du musst Dir wirklich einfach nur ein Bild vom Funktionsablauf malen. Dann wird es doch klar, was da passiert. Und immer dran denken: in PHP sind 'Arrays' als Baumstrukturen (verkettete Listen mit Seitenarmen) aufgebaut.

        Harzliche Grüße aus http://www.annerschbarrich.de

        Tom

        --
        Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
        Nur selber lernen macht schlau
      3. Ich bin immer noch nicht davon überzeugt, dass du dir mit dem großen Datenhaufen einen Gefallen tust. Es wird auch nicht besser, wenn du den großen Datenhaufen erst auf Überflüssiges hin auswertest, um dann die den kleineren Datenhaufen nach anderen Kriterien auszuwerten.

        Ja, lieber wäre mir auch ich müsste nicht alles
        unnötige (leere Arrays/werte) über den Browser senden.
        Aber leider fällt mir keine Alternative ein und so leistet
        mir diese Funktion jetzt doch schon Einiges an Scriptreduzierung.

        Nein nein, dies ist nicht dein eigentliches Problem. Ich hab noch mal über dein Problem im Ganzen nachgedacht. Du möchtest eine Simulation mit jeder Menge Eingabeparameter programmieren. Das sieht, deiner Schilderung der jetzigen Datenstruktur nach, sehr umfangreich aus. Deswegen ist es wichtig, die Übersicht bewahren zu können, spricht eine Ordnung hineinzubringen und die ganze Sache gut vorzubereiten, damit du nicht auf halbem Wege feststellen musst: Alles Sch...lecht geplant! Und dann alles in kleinen Schritten zusammenbauen, das ergibt dann viele kleine Erfolgserlebnisse und die sind auch wichtig! :-)

        Lassen wir mal die Auswertung der Eingabeparameter außen vor und betrachten die ganze Geschichte aus der Benutzerseite.

        Teil 1 - Der Anwender
        ---------------------

        Auch ihm mutest du zu, sich durch einen riesigen Berg von Optionen zu kämpfen. Das stelle ich mir ermüdend und wenig erbaulich vor. Wie soll das ablaufen, wenn er die Simulation ein weiteres Mal mit etwas geänderten Daten ausführen möchte? Sollen dann nicht vielleicht die vorigen Änderungen gleich erhalten bleiben, so dass er nur wenig anzupassen hat? Vielleicht merkt er auch gar nicht, dass er eine Option vergessen hat, die er vorhin schon drin hatte. Das erfordert auch auf seiner Seite eine hohe Konzentration, aber eigentlich will er sich ja auf die Simulation und deren Ergebnis konzentrieren.

        Du solltest als erstes einmal deine Parameter sinnvoll gruppieren. (Denk jetzt nicht an die Auswertung, die kommt später.) Welche Parameter gehören zusammen? Hier gilt es den besten Kompromiss zwischen der Anzahl der Gruppen und der Anzahl der Optionen pro Gruppe zu finden. Gegebenenfalls musst du da Untergruppen verwenden.

        Gruppe A
           Option A.1
           Option A.2
           ...
        Gruppe B
         Untergruppe B1
           Option B1.1
           Option B1.2
           ...
         Untergruppe B2
           ...
        Gruppe C
           ...

        Wenn du diese Struktur hast musst du dir Gedanken machen, wie die Navigation für den Anwender aussehen soll (auch hier die Auswertelogik erstmal außen vor lassen). Für jede Options-Gruppe erhält der Anwender eine eigene Seite. Wenn er die Seite ein weiteres Mal aufruft, findet er seine eben gemachten Einstellungen wieder vor und kann weitere Änderungen vornehmen. Das gleiche passiert auf den anderen Seiten. Natürlich müssen diese Eingaben irgendwo zwischengespeichert werden. Und vielleicht soll er seine Daten auch morgen wieder vorfinden, wenn er weitersimulieren will...

        "Start der Simulation" sollte ein eigener Menüpunkt sein. Mehr dazu später.

        Welche weiteren Funktionalitäten sind aus Sicht des Benutzers sinvoll?

        Wenn du das alles zusammengetragen hast, kannst du schon mal von der Theorie in die Praxis wechseln und die Benutzeroberfläche zusammenbauen. Dass bestimmte Funktionalitäten noch nicht implementiert sind, ist dabei erstmal nebensächlich. Schnickschnack kommt später. Erstmal muss für den Anwender im Groben erkennbar sein, was er alles tun kann.

        Teil 2 - Die Simulation
        -----------------------

        Nun musst du die Simulation genauso sorgfältig planen. Welche Aufgaben gibt es zu erfüllen? Kann man diese Teile sinnvoll kapseln? Welche Parameter benötigt welcher Teil? Welches Ergebnis produziert er? Welche Daten müssen von einem Teil einem anderen übergeben werden? _Wie_ diese Kommunikation/Parameterübergabe zwischen den Teilen stattfinden soll, ist im Moment nicht so wichtig. Nur das: _Wer_ kommuniziert _was_ mit _wem_? Dabei ist ein direktes Übergreifen eines Teiles in einen anderen unbedingt zu vermeiden, spricht: der eine darf keine Parameter des anderen direkt manipulieren. Auch nicht, wenn es sich um globale Dinge handelt. Wenn das notwendig ist muss eine globale Instanz mit eingeplant werden, die sich darum kümmert. Jeder Teil darf also nur Aufgaben an einen anderen Teil weiterdeligieren. Dazu kann er Parameter übergeben und bekommt gegebenenfalls wieder etwas zurück.

        Teil 3 - Funktionalitäten
        -------------------------

        An dieser Stelle kann ich dir nur grob die Richtung weisen, wie das eine oder andere zu implementieren geht.

        Zum Zusammentragen der Eingabe-Optionen und zum Zwischenspeichern zwecks späterem Wiederverwenden bieten sich in PHP Sessions an. Hier muss es erst einmal darum gehen, die Daten einzusammeln und für andere Programmteile bereitzustellen. Eine Auswertung erfolgt nur für die Richtigkeit der Eingabedaten. Wird eine Zahl erwartet wird nur überprüft, ob es eine ist und ob der Wertebereich stimmt. Sind Pflichtfelder innerhalb der Gruppe ausgefüllt? Stimmen die Abhängigkeiten innerhalb der Gruppe? Sind Abhängigkeiten gruppenübergreifend zu prüfen, ist sicher die Gruppenaufteilung nicht richtig gewählt. Dann solltest du lieber das korrigieren als zu kompliziertes Übergreifendes behandeln zu wollen.
        Stimmt alles kannst du die Optionsgruppe für diese Seite als geprüft markieren.

        Nachdem der Benutzer die Daten zusammengetragen hat kann er die Simulation starten.
        Hier erfolgt zunächst eine Prüfung ob alle Optionsgruppen als korrekt markiert sind. Dann kann der Hauptteil der Simulation starten und die entsprechenden Aufgaben samt Parametern an die einzelnen Teile zur Ausführung geben, die Ergebnisse einsammeln, diese weiterreichen und so weiter und so fort. Irgendwann ist alles berechnet und kann zur Ausgabe gebracht werden.

        Teil 4 - Die Werkzeuge
        ----------------------

        An dieser Stelle habe ich Bedenken dir bestimmte Sachen zu empfehlen, da ich dich so einschätze, dass du noch nicht genügend Programmier- im allgemeinen und PHP-Erfahrung im speziellen hast.

        Für die Optionsformulare bietet sich eine Technik namens "Affenformular" an. Das ganze gibt es ausgereift im PEAR-Paket HTML_QuickForm (Kurzanleitung, leider nur in englisch). PEAR wird teilweise bei der Installation von PHP bereits mit installiert.

        Das kümmert sich um die Formulardarstellungs- und Eingabelogik inklusiver gewisser Prüfungen (Pflichtfelder etc.). Es liefert die Eingaben als Array, und dieses kann man auch später wieder so wie es ist dem Formular zur erneuten Bearbeitung übergeben.
        Du kannst das so nutzen, indem du dieses Array in der Session speicherst und wenn der Benutzer die Formularseite erneut aufruft kannst du ihm seine früheren Eingaben wieder vorsetzen, und das alles mit übersichtlichem Programmieraufwand.

        Ich hoffe dir damit den "richtigen" Weg gezeigt zu haben, wie du dein umfangreiches Projekt für dich und für andere übersichtlich gestalten kannst.
        Und noch eine Bitte: Bringe etwas Ordnung in deinen Quelltext. Verwende Einrückungen, so wie es beispielsweise in dem Affenformular-Beispiel der Fall ist. Das erhöht die Lesbarkeit, deine Projektquelltextleser und Helfenden werden es dir danken.

        dedlfix

        1. Hallo Dedlfix,
          das war ja schon ein halber Roman ;-)
          Vielen Dank für so viel Mühe.

          Aber ich kann Dir versichern, dass diese Simulation sehr
          wohl gut durchdacht ist und die aktuelle Prroblematik allein
          aus der enormen Fülle von Optionen herrührt.

          Du empfiehlst die Optionen in Gruppen auf einzelne
          Seiten zu verteilen. Ja das ist auch jetzt schon so
          aber Diese noch feiner zu gruppieren, wäre nicht im Sinne
          des Users, weil er dann zu viel blättern müsste, obwohl
          er dann von 10 Seiten viell. nur 2 nutzen muss.
          Die Übersichtlichkeit und das Handling sind auch bei 100
          Optionen klar erkennbar, weils intuitiv zu bedienen ist.

          Einziges Manko ist und bleibt, aber davon merkt der User
          nichts: die grosse Datenflut von Leeren Optionen.

          An dieser Stelle habe ich Bedenken dir bestimmte Sachen zu empfehlen, da ich dich so einschätze, dass du noch nicht genügend Programmier- im allgemeinen und PHP-Erfahrung im speziellen hast.

          So schätzt Du mich ein ?
          So ein Bild erwecken mein unzähligen Foreneinträge hier
          im Archiv ?  ;-))

          Na ja, soganannte Affenformulare gehören zu meinem Standard
          als andere noch nicht daran gedacht haben. Ich habe bisher
          komplexe Anwendungen im Bereich Börse, Logistik, Zahlungssysteme, CAD, etc
          geschaffen. Bei allen Anwendungen versuche ich die
          Administration so zu automatisieren , dass unter Umständen
          eine einzige Person das ganze bedienen kann.
          Herbei bin ich mir leider oft selbst im Weg, wenn ich
          es mit Gewalt automatisierter haben will ;-)

          Referenzen: Grosse Teile von www.stockhouse.com sind
          auf meinen Mist gewachsen, ebenso new.stockwatch.com ;-)

          Und noch eine Bitte: Bringe etwas Ordnung in deinen Quelltext. Verwende Einrückungen, so wie es beispielsweise in dem Affenformular-Beispiel der Fall ist. Das erhöht die Lesbarkeit, deine Projektquelltextleser und Helfenden werden es dir danken.

          Ja das ist so ein Ding mit dem Einrücken,
          am Anfang hatte ich mich bemüht so zu schreiben, weil
          es als sauberer Programmierstil gilt. Der Nachteil dabei ist,
          wenn man diesem Stil konsequent treu bleibt, dass weiterführende
          Verschachtelungen den Quelltext immer weiter nach rechts treiben,
          so dass nicht mehr alles ohne zu scrollen sauber im Bild ist.
          (Es sei denn Zeilenumbrüche, was noch furchtbarer ist)

          So habe ich mich irgendwann entschlossen, alles nach
          links zu rücken und einzelene Bereiche durch Nummern abzugrenzen.
          Bsp.

          start of 1

          ....

          End of 1

          usw. Das hat sich für mich persönlich als
          besten Stil behauptet.

          Gruss und Dank
          Nicole

          ps. Kleiner Tipp für Deine Mühe, wenn Du Aktien handelst:
          Starfire Minerals Inc.
          Wertpapierkennnummer : 784 574
          Viel Erfolg

          1. Einziges Manko ist und bleibt, aber davon merkt der User
            nichts: die grosse Datenflut von leeren Optionen.

            Ich kann deine Gedankengänge nicht nachvollziehen. Du machst dir erst die Mühe, die Leerdaten zu erkennen und zu beseitigen. Wie schaust du dann nach welche Optionen gesetzt sind?

            Warum machst das nicht gleich so: (Verschachtelungen mal außen vorgelassen)

            foreach ($optionen as $option) {
              if (empty($option))
                continue;

            switch ($option) {
                case wert1:
                  ...
                case wert2:
                  ...
              }
            }

            An dieser Stelle habe ich Bedenken dir bestimmte Sachen zu empfehlen, da ich dich so einschätze, dass du noch nicht genügend Programmier- im allgemeinen und PHP-Erfahrung im speziellen hast.

            So schätzt Du mich ein ?
            So ein Bild erwecken mein unzähligen Foreneinträge hier im Archiv ?  ;-))

            Wer liest schon das Archiv? :-) Nein dieses Bild liefert die Art und Weise, wie du hier versuchst Probleme kleinzuklopfen anstatt sie gleich ordentlich anzugehen.

            Und noch eine Bitte: Bringe etwas Ordnung in deinen Quelltext. Verwende Einrückungen, so wie es beispielsweise in dem Affenformular-Beispiel der Fall ist. Das erhöht die Lesbarkeit, deine Projektquelltextleser und Helfenden werden es dir danken.

            Ja das ist so ein Ding mit dem Einrücken,
            am Anfang hatte ich mich bemüht so zu schreiben, weil
            es als sauberer Programmierstil gilt. Der Nachteil dabei ist,
            wenn man diesem Stil konsequent treu bleibt, dass weiterführende
            Verschachtelungen den Quelltext immer weiter nach rechts treiben,
            so dass nicht mehr alles ohne zu scrollen sauber im Bild ist.

            Solche Monsterverschachtelungen wirken auch nicht grad professionell auf mich. Dein Problem ist nicht, diese Einrückungen nicht darstellen zu können, sondern solch eine Verschachtelungstiefe zu vermeiden.

            So habe ich mich irgendwann entschlossen, alles nach
            links zu rücken und einzelene Bereiche durch Nummern abzugrenzen.
            Bsp.

            start of 1

            ....

            End of 1

            usw. Das hat sich für mich persönlich als
            besten Stil behauptet.

            Das sehe ich anders. Wenn ich programmiere kozentriere ich mich auf die Aufgabenstellung, dann will ich nicht auch noch den den Überblick behalten müssen, zwischen welchen Nummern ich mich befinde. Das muss einfach leicht zu erkennen sein, beispielsweise durch die Optik.

            Insgesamt machst du mir den Eindruck, dass du deine Probleme nur umlagerst, statt sie richtig anzugehen.

            dedlfix