mehrdimensionales Array durchduchen

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • mehrdimensionales Array durchduchen

    Hallo zusammen,

    ich habe ein mehrdimensionales Array (siehe nachfolgend) und suche eine Funktion, die mir ausgibt, in welcher Zeile ein bestimmter Wert - zum Beispiel: 1420484634 - steht.

    (Anschließend soll diese Zeile gelöscht werden, diese Funktion ist mir bekannt)

    PHP-Code:
    $fahrten = array(
    array(
    "code" => 1420484575"datum" => 20150107"strecke" => "PL->A""vorname" => "Thomas"),
    array(
    "code" => 1420484634"datum" => 20150107"strecke" => "PL->A""vorname" => "Michael"),
    array(
    "code" => 1420484676"datum" => 20150107"strecke" => "PL->A""vorname" => "Kersten"),
    array(
    "code" => 1420484700"datum" => 20150107"strecke" => "PL->A""vorname" => "Matthias"),
    array(
    "code" => 1420484926"datum" => 20150112"strecke" => "PL->A""vorname" => "Michael"),
    ); 
    es bedankt sich im Voraus:
    der php-Autodidakt Thomas

  • #2
    So eine Funktion gibts nicht. Die musst du wohl selber schreiben.

    Kommentar


    • #3
      Zitat von wspl Beitrag anzeigen
      ich habe ein mehrdimensionales Array (siehe nachfolgend) und suche eine Funktion, ..... - zum Beispiel: 1420484634 - steht.
      (Anschließend soll diese Zeile gelöscht werden.....)
      Das geht in einem Rutsch!

      Das Zauberwort ist array_filter()
      Wir werden alle sterben

      Kommentar


      • #4
        Zitat von combie Beitrag anzeigen
        Das geht in einem Rutsch!

        Das Zauberwort ist array_filter()
        Nicht wirklich, denn array_filter() erzeugt eine Kopie des Ausgangs-Arrays, in der bestimmte Einträge des Ausgangs-Arrays enthalten sind. Der OP wollte aber im Ausgangs-Array einen bestimmten Eintrag finden und löschen.

        Zitat von wspl Beitrag anzeigen
        Hallo zusammen,

        ich habe ein mehrdimensionales Array (siehe nachfolgend) und suche eine Funktion, die mir ausgibt, in welcher Zeile ein bestimmter Wert - zum Beispiel: 1420484634 - steht.

        ...

        PHP-Code:
        $fahrten = array(
        array(
        "code" => 1420484575"datum" => 20150107"strecke" => "PL->A""vorname" => "Thomas"),
        array(
        "code" => 1420484634"datum" => 20150107"strecke" => "PL->A""vorname" => "Michael"),
        // ...
        ); 
        Willst du wirklich nur nach einem skalaren Wert wie einer Ganzzahl suchen? Es ist möglich, dass bspw. die Einträge für "code" oder "datum" eine Zahl enthalten, die in beiden Kategorien vorkommen kann. Du kannst dann nicht unterscheiden, in welcher der Suchbegriff gefunden wurde.

        Für ganze Datensätze gehen Suchen und Ersetzen so:

        PHP-Code:
        // nach (exakt) diesem Datensatz suchen:      
        $needle = array (
            
        'code' => 1420484634,
            
        'datum' => 20150107,
            
        'strecke' => 'PL->A',
            
        'vorname' => 'Michael',
        );

        $keys array_keys($fahrten$needletrue);
                
        // Datensatz für ersten gefundenen Schlüssel löschen:
        if (!empty ($keys)) {
            unset (
        $fahrten[reset($keys)]);

        oder auch so:

        PHP-Code:
        // nach (exakt) diesem Datensatz suchen:      
        $needle = array (
            
        'code' => 1420484634,
            
        'datum' => 20150107,
            
        'strecke' => 'PL->A',
            
        'vorname' => 'Michael',
        );

        $key array_search($needle$fahrtentrue);
                
        // Datensatz für ersten gefundenen Schlüssel löschen:
        if ($key || false !== $key || null !== $key) {
            unset (
        $fahrten[$key]);

        Wenn du nicht nach ganzen Datensätzen sondern nach spezifischen Eigenschaften darin suchen willst, wird es komplizierter. Du musst dir die Suchfunktion selbst bauen:

        PHP-Code:
        function array_search_by_userfunc($haystack$cmpfunc$compare_with) {
            foreach (
        $haystack as $key => $record) {
                if (
        $cmpfunc($record$compare_with)) {
                    return 
        $key;
                }
            }
            return 
        null;

        Das liegt daran, dass PHP keine Array-nach-Key-Suchfunktion eingebaut hat, die mit einem benutzerdefinierten Vergleich zurechtkommt.

        Eine benutzerdefinierte Vergleichsfunktion für deinen speziellen Fall könnte so aussehen:

        PHP-Code:
        // vergleicht zwei Arrays
        $cmpfunc = function (
            
        $item// das zu untersuchende Array
            
        $compare_with // das Vergleichs-Array
        ) {
            
        // "intersect": holt die Array-Bestandteile, die sich in beiden Arrays finden und gleich sind
            // "assoc": nur Eintraege mit gleichen Schluesseln werden verglichen
            
        return count($compare_with) === count(array_uintersect_assoc(
                
        $compare_with,
                
        $item,
                function (
        $a$b) {
                    
        // sufficient for finding equals (, but not for sorting!)
                    
        return $a === $b 1;
                }
            ));

        Angewendet könnte das dann so aussehen:

        PHP-Code:
        $what_to_find = array (
            
        'code' => 1420484634,
        );

        $key array_search_by_userfunc($fahrten$cmpfunc$what_to_find); 
        ... und weil das Ganze nicht allen wirklich gefällt, wurden für diese Anwendungszwecke Datenbanken erfunden.
        Zuletzt geändert von fireweasel; 07.01.2015, 14:36. Grund: typos: Smiley + array_search() + array_keys()
        Klingon function calls do not have “parameters”‒they have “arguments”‒and they always win them!

        Kommentar


        • #5
          Nicht wirklich, denn array_filter() erzeugt eine Kopie des Ausgangs-Arrays, in der bestimmte Einträge des Ausgangs-Arrays enthalten sind. Der OP wollte aber im Ausgangs-Array einen bestimmten Eintrag finden und löschen.
          PHP-Code:
          $fahrten array_filter($fahrten, .....); 
          Für mich will er in der Code Spalte suchen...
          (und nirgendwo anders)
          Und selbst wenn, kann man die Lambdafunktion um jede Spielerei erweitern.
          Wir werden alle sterben

          Kommentar


          • #6
            Zitat von combie Beitrag anzeigen
            PHP-Code:
            $fahrten array_filter($fahrten, .....); 
            Für mich will er in der Code Spalte suchen...
            Zitat von wspl Beitrag anzeigen
            (Anschließend soll diese Zeile gelöscht werden, ...
            (und nirgendwo anders)
            Für mich sieht es eher so aus, dass er noch nicht richtig weiß, wonach er eigentlich suchen möchte: einzelne Felder ("column", "Spalte", ...) vs. ganzer Datensatz("row", "Zeile", "Unter-Array", ...).

            Und selbst wenn, kann man die Lambdafunktion um jede Spielerei erweitern.
            Array_filter() übergibt der Callback-Funktion kein Argument, mit der diese auf die Keys des durchzulaufenden Arrays zugreifen könnte und dafür ist sie auch nicht gedacht. Meintest du vielleicht array_walk()?
            Klingon function calls do not have “parameters”‒they have “arguments”‒and they always win them!

            Kommentar


            • #7
              Meintest du vielleicht array_walk()?
              Nein.
              Das kann Array Einträge ändern, aber nicht Datensätze mit code = 1420484634 entfernen.


              Array_filter() übergibt der Callback-Funktion kein Argument, mit der diese auf die Keys des durchzulaufenden Arrays zugreifen könnte
              Braucht sie auch nicht.
              Die, für die Suche nötigen, konstanten Parameter kann man per USE übergeben.
              Wir werden alle sterben

              Kommentar


              • #8
                Den Wert "code" habe ich extra dafür in das Array aufgenommen, dass er eindeutig (einmalig) ist. Es ist einfach der Zeitstempel des Zeitpunktes, an dem ein User ein Formular abschickt und damit diese Zeile erstellt
                .
                Dass dieser Wert ein weiteres mal vorkommt, wäre sehr großer Zufall. Bei dem übersichtlichen Nutzerkreis unserer kleinen internen Mitfahrzentrale (ca. 30 User), eigentlich auszuschließen.

                Ich werde mir eure Vorschläge anschauen und testen. Bei Bedarf melde ich mich nochmal, vielen Dank euch allen erstmal.

                Thomas

                Kommentar


                • #9
                  Nachtrag:
                  Nur mal so als Beispiel, dass es mit array_filter und einem Closure ganz gut funktioniert....
                  Auch mit flexieblem Spaltenbezeichner....

                  PHP-Code:
                  <?php
                  error_reporting
                  (-1);
                  ini_set('display_errors'TRUE);
                   

                  function 
                  drop_from_2dim_array(Array &$array,$spalte,$value)
                  {
                               
                      
                  $array array_filter($array,
                                function (
                  $datensatz) use($spalte,$value)
                                {
                                  return 
                  $datensatz[$spalte] !== $value;
                                }
                              );
                  }


                  $fahrten = array( 
                                      array(
                  "code" => 1420484575"datum" => 20150107"strecke" => "PL->A""vorname" => "Thomas"), 
                                      array(
                  "code" => 1420484634"datum" => 20150107"strecke" => "PL->A""vorname" => "Michael"), 
                                      
                  // ... 
                                 
                  );



                  var_dump($fahrten);
                  drop_from_2dim_array($fahrten,'code',1420484575);
                  echo 
                  '<br>';
                  var_dump($fahrten);
                  Wir werden alle sterben

                  Kommentar


                  • #10
                    Meintest du vielleicht array_walk()?
                    Nein.
                    Das kann Array Einträge ändern, aber nicht Datensätze mit code = 1420484634 entfernen.
                    Soll es auch nicht. Aber es reicht, um ein Array zu durchsuchen und den gewünschten Key (die Zeilen- oder Datensatznummer) zurückzuliefern, die benötigt wird, um anschließend die entsprechende Zeile (den Datensatz) löschen zu können.

                    Zitat von combie Beitrag anzeigen
                    Nachtrag:
                    Nur mal so als Beispiel, dass es mit array_filter und einem Closure ganz gut funktioniert....
                    Dass es damit irgendwie funktioniert, habe ich nicht bestritten. Aber es ist umständlich, ineffizient und irgendwie an der Aufgabenstellung vorbei.

                    PHP-Code:
                    <?php
                    error_reporting
                    (-1);
                    ini_set('display_errors'TRUE);
                     

                    function 
                    drop_from_2dim_array(Array &$array,$spalte,$value)
                    {
                                 
                        
                    $array array_filter($array,
                                  function (
                    $datensatz) use($spalte,$value)
                                  {
                                    return 
                    $datensatz[$spalte] !== $value;
                                  }
                                );
                    }

                    //...
                    Du erzeugst also erst eine gefilterte Kopie des Original-Arrays um mit dieser anschließend das Original zu überschreiben? Warum? Falls kein passender Eintrag gefunden wird, erstellst du eine komplette Kopie, belegst also unötigerweise die doppelte Menge an Speicher, nur um array_filter() benutzen zu können. Nebenbei zerstörst du so die Key-Value-Zuordnung des Original-Arrays, selbst wenn es darin nichts zu löschen gab. Nicht dass das den OP stören würde, aber elegant|effizient ist was anderes.
                    Zuletzt geändert von fireweasel; 06.01.2015, 18:34. Grund: typos
                    Klingon function calls do not have “parameters”‒they have “arguments”‒and they always win them!

                    Kommentar


                    • #11
                      OffTopic:
                      Öhmmm...
                      Mich deucht, du übertreibst....
                      Errege dich, wenn du magst....

                      Ich hoffe, dich stört es nicht, wenn mich dieses langweilt ....
                      Wir werden alle sterben

                      Kommentar


                      • #12
                        Na ja, letztlich ist es eine Performance-Geschichte. Und für die gilt eben traditionell: Wenn es kein Problem gibt, gibt es kein Problem. *schulterzuck* Man kann über Laufzeiteigenschaften natürlich gern reden (ist auch ein sehr interessantes – und undankbares – Thema), aber ob es wirklich eine praktische Rolle spielt, ist immer die andere Frage. (Okay, das war jetzt sicher kein Geheimnis. )

                        FWIW: Ich glaube, Varianten, in denen nur eine Core-Funktion mit einem Callback/Closure aufgerufen wird, sind leider trotzdem immer langsam wie sonst was (wegen der Closure).

                        Zitat von wspl Beitrag anzeigen
                        Den Wert "code" habe ich extra dafür in das Array aufgenommen, dass er eindeutig (einmalig) ist. Es ist einfach der Zeitstempel des Zeitpunktes, an dem ein User ein Formular abschickt und damit diese Zeile erstellt
                        .
                        Dass dieser Wert ein weiteres mal vorkommt, wäre sehr großer Zufall. Bei dem übersichtlichen Nutzerkreis unserer kleinen internen Mitfahrzentrale (ca. 30 User), eigentlich auszuschließen.
                        Sorry, aber sauber ist das trotzdem nicht.

                        Noch eine simple Variante zur Fragestellung:

                        PHP-Code:
                        $search 1420484634;
                        $found false;

                        foreach (
                        $fahrten as $key => $element) {
                            if (
                        $search === $element['code']) {
                                
                        $found true// Umweg, weil es selten eine gute Idee ist,
                                               // in PHP das per foreach durchlaufene Element zu verändern
                                
                        break;
                            }
                        }

                        if (
                        $found) {
                            unset(
                        $fahrten[$key]);

                        Zuletzt geändert von mermshaus; 07.01.2015, 06:49.

                        Kommentar


                        • #13
                          erstellst du eine komplette Kopie, belegst also unötigerweise die doppelte Menge an Speicher,
                          Foreach arbeitet (intern) auch mit einer Kopie des Arrays.
                          (soweit mir bekannt)

                          belegst also unötigerweise die doppelte Menge an Speicher,
                          Welches die automatische Müllabfuhr blendend wieder bereinigt.


                          Nebenbei zerstörst du so die Key-Value-Zuordnung des Original-Arrays, selbst wenn es darin nichts zu löschen gab.
                          Da muss ich dich leider enttäuschen.
                          Ob es löscht, oder nicht, die Key Zuordnung bleibt erhalten.
                          Zumindest bei meinem PHP .....

                          sind leider trotzdem immer langsam wie sonst was
                          Naja... 0.5 Sekunden Laufzeitdifferenz bei je 10000 Durchläufen...
                          Wir sprechen also über Zeiten, Optimierungen, welche weit unter einem tausendstel einer Sekunde liegen. (auf meinem ollen 32Bit Klapperkasten)
                          Ich könnte mir Situationen vorstellen, in welchen das wichtig ist. Aber hier vermutlich nicht.

                          @mermshaus
                          Musste deins leicht modifizieren um (halbwegs) gerecht testen zu können

                          PHP-Code:
                          function drop_from_2dim_array(Array &$array,$spalte,$value)
                          {
                                
                          $found false;
                                
                                foreach (
                          $array as $key => $element
                                {
                                    if (
                          $value === $element[$spalte]) 
                                    {
                                        
                          $found true// Umweg, weil es selten eine gute Idee ist,
                                                       // in PHP das per foreach durchlaufene Element zu verändern
                                        
                          break;
                                    }
                                }
                                if (
                          $found)  unset($array[$key]);

                          Unterschiede:
                          Deins bricht beim ersten Fund ab.
                          Meins ackert immer alles durch.

                          Was richtig und gewünscht ist, kann ich nicht beurteilen.



                          Grundsätzlich würde ich bei solchen Problemstellungen eher auf Datenbanken zurückgreifen. Und dem entsprechend auf automatisch vergebene IDs setzen und nicht auf wackelige Zeitstempel.
                          Wie sich das auf die Laufzeiten auswirken würde, will ich gar nicht wissen
                          Zuletzt geändert von combie; 07.01.2015, 12:14.
                          Wir werden alle sterben

                          Kommentar


                          • #14
                            Öhmmm...
                            Mich deucht, du übertreibst....
                            Mir deucht, du überschätztest das Ausmaß meiner Übertreibung.
                            Ich wollte nur ein wenig spielen.
                            Errege dich, wenn du magst....
                            Nicht "erregt", nur ein wenig verwundert. Ich empfand deine bisherigen Beiträge immer als lehrreich.
                            Ich hoffe, dich stört es nicht, wenn mich dieses langweilt ....
                            Nö, es langweilt mich ja auch schon ...
                            Aber man ist ja froh, wenn hier überhaupt noch etwas passiert ...

                            Zitat von combie Beitrag anzeigen
                            Foreach arbeitet (intern) auch mit einer Kopie des Arrays.
                            (soweit mir bekannt)
                            Ähmm, nein ... es erzeugt lediglich eine Kopie des Array-"Zeigers", der Rest wird referenziert. (soweit mir bekannt)

                            Welches die automatische Müllabfuhr blendend wieder bereinigt.
                            Nur, wenn das Script nicht schon vorher beendet wurde, weil der Speicher nicht ausreichte. Aber das passiert vermutlich in diesem simplen Beispiel nicht.

                            Mir ging es nicht um (Micro-)Optimierung, sondern um Aufgabentreue. Guckstu:

                            Aufgabenstellung:
                            * Finde Array-Eintrag und lösche ihn.

                            Array_filter()-Lösung:
                            * Finde alle Einträge, die nicht dem Suchkriterium entsprechen.
                            * Kopiere diese in ein temporäres Array.
                            * Überschreibe anschließend damit das Ursprungs-Array (das deswegen extra in der Parameterliste per & referenziert werden muss).

                            Das finde ich ein klein wenig über das Ziel hinausgeschossen, wenn es im Idealfall array_search() und unset() auch getan hätten.

                            ...
                            Am Speichermanagement von PHP rockt eher wenig.

                            Da muss ich dich leider enttäuschen.
                            Ob es löscht, oder nicht, die Key Zuordnung bleibt erhalten.
                            Zumindest bei meinem PHP .....
                            Stimmt, du hast recht: "... Array keys are preserved."

                            Array_filter() gibt also ein Array zurück, das genauso strukturiert ist, wie eines, aus dem man die Einträge von Hand (per unset()) gelöscht hat.

                            Ich bin bisher davon ausgegangen, dass es das Array als Liste betrachtet und nicht als assoziatives Array.

                            *Wieder_was_gelernt*

                            Grundsätzlich würde ich bei solchen Problemstellungen eher auf Datenbanken zurückgreifen. Und dem entsprechend auf automatisch vergebene IDs setzen und nicht auf wackelige Zeitstempel.
                            Deswegen hatte ich das in meinem ersten Beitrag auch schon erwähnt ... ganz unten am Ende ...
                            Zuletzt geändert von fireweasel; 07.01.2015, 14:42. Grund: fixed: missverständliche formulierung + smiley-syntax
                            Klingon function calls do not have “parameters”‒they have “arguments”‒and they always win them!

                            Kommentar


                            • #15
                              foreach kopierte im Jahr 2011 ungefähr unter diesen Umständen:

                              - PHP internals: When does foreach copy?

                              Ob das „heutzutage“ (geht ja eher um die PHP-Version) noch immer so ist – keinen blassen Schimmer. Unter anderem so was meinte ich mit undankbaren Diskussionen über Laufzeiteigenschaften. Es kann immer gut sein, dass da intern von einer Version auf die nächste was „optimiert“ wird.

                              In meiner Variante wird das Array jedenfalls nicht kopiert (getestet mit memory_get_peak_usage()).

                              Die Geschichte mit dem $found bei mir existiert aber auch deshalb, weil PHP mehr Speicher verbraucht hat, als das unset() direkt innerhalb der foreach-Schleife stand. – Zumindest dann, als der Code wiederum nicht innerhalb einer Funktion stand, der $fahrten per Referenz übergeben wird.

                              Das hier…

                              PHP-Code:
                              function merms(&$fahrten$search)
                              {
                                  foreach (
                              $fahrten as $key => $element) {
                                      if (
                              $search === $element['code']) {
                                          unset(
                              $fahrten[$key]);
                                          break;
                                      }
                                  }

                              …verbraucht keinen zusätzlichen Speicher.

                              Da er aber auch nicht wirklich stört, würde ich den Umweg mit $found dennoch drinlassen. Nicht zuletzt auch deshalb, weil derlei Beobachtungen eben auch fragil sind.

                              Zitat von combie
                              Naja... 0.5 Sekunden Laufzeitdifferenz bei je 10000 Durchläufen...
                              Wir sprechen also über Zeiten, Optimierungen, welche weit unter einem tausendstel einer Sekunde liegen. (auf meinem ollen 32Bit Klapperkasten)
                              Das hängt davon ab, wie man den Benchmark aufbaut. Wenn du da 10000 Aufrufe der zu testenden Funktion hast und ein Array mit nur 5 Elementen und wenn du dazu auch noch pro Durchlauf das Array neu initialisiert (weil ja ein Element entfernt wird), dann misst du zum Beispiel eine ganze Menge Overhead mit, der für jeden Test die gleiche konstante Zeit dauert.

                              Ich habe es mal so getestet:

                              PHP-Code:
                              <?php

                              function merms(array &$fahrten$search)
                              {
                                  
                              $found false;

                                  foreach (
                              $fahrten as $key => $element) {
                                      if (
                              $search === $element['code']) {
                                          
                              $found true;
                                          break;
                                      }
                                  }

                                  if (
                              $found === true) {
                                      unset(
                              $fahrten[$key]);
                                  }
                              }

                              function 
                              combie(array &$fahrten$search)
                              {
                                  
                              $fahrten array_filter($fahrten,
                                      function (
                              $datensatz) use ($search) {
                                          return 
                              $datensatz['code'] !== $search;
                                      }
                                  );
                              }

                              $fahrten = array();

                              for (
                              $i 0$i 100$i++) {
                                  
                              $fahrten[] = array("code" => 1420484575"datum" => 20150107"strecke" => "PL->A""vorname" => "Thomas" $i);
                                  
                              $fahrten[] = array("code" => 1420484676"datum" => 20150107"strecke" => "PL->A""vorname" => "Kersten" $i);
                                  
                              $fahrten[] = array("code" => 1420484700"datum" => 20150107"strecke" => "PL->A""vorname" => "Matthias" $i);
                                  
                              $fahrten[] = array("code" => 1420484926"datum" => 20150112"strecke" => "PL->A""vorname" => "Michael" $i);

                                  
                              // Stelle, an der gesuchtes Element eingefügt wird
                                  
                              if ($i === 75) {
                                      
                              $fahrten[] = array("code" => 1420484634"datum" => 20150107"strecke" => "PL->A""vorname" => "Michael");
                                  }
                              }



                              var_dump(memory_get_peak_usage());                     // Speicherverbrauch vor Funktion

                              $search 1420484634;
                              $check  count($fahrten);
                              $start  microtime(true);

                                                                                     
                              // Hier eine Funktion einkommentieren
                              #merms($fahrten, $search);
                              combie($fahrten$search);

                              var_dump(((microtime(true) - $start) * 1000) . ' ms'); // Laufzeit
                              var_dump(count($fahrten) === $check 1);              // Eins entfernt?
                              var_dump(memory_get_peak_usage());                     // Speicherverbrauch nach Funktion
                              Da komme ich auf meinem System immer mindestens auf einen Faktor von 3. Je weiter vorne das gesuchte Element steckt, desto extremer wird natürlich der Unterschied.

                              Dein Ansatz scheint übrigens nicht die doppelte Menge an Speicher zu verbrauchen. Da wird intern wohl noch irgendwas optimiert.

                              Ich könnte mir Situationen vorstellen, in welchen das wichtig ist. Aber hier vermutlich nicht.
                              Joa, Performance-Thema eben…

                              Kommentar

                              Lädt...
                              X