mehrdimensionales Array durchduchen

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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.

    Comment


    • #3
      Originally posted by wspl View Post
      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

      Comment


      • #4
        Originally posted by combie View Post
        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.

        Originally posted by wspl View Post
        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.
        Last edited by fireweasel; 07-01-2015, 13:36. Reason: typos: Smiley + array_search() + array_keys()
        Klingon function calls do not have “parameters”‒they have “arguments”‒and they always win them!

        Comment


        • #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

          Comment


          • #6
            Originally posted by combie View Post
            PHP Code:
            $fahrten array_filter($fahrten, .....); 
            Für mich will er in der Code Spalte suchen...
            Originally posted by wspl View Post
            (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!

            Comment


            • #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

              Comment


              • #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

                Comment


                • #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

                  Comment


                  • #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.

                    Originally posted by combie View Post
                    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.
                    Last edited by fireweasel; 06-01-2015, 17:34. Reason: typos
                    Klingon function calls do not have “parameters”‒they have “arguments”‒and they always win them!

                    Comment


                    • #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

                      Comment


                      • #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).

                        Originally posted by wspl View Post
                        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]);

                        Last edited by mermshaus; 07-01-2015, 05:49.

                        Comment


                        • #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
                          Last edited by combie; 07-01-2015, 11:14.
                          Wir werden alle sterben

                          Comment


                          • #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 ...

                            Originally posted by combie View Post
                            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 ...
                            Last edited by fireweasel; 07-01-2015, 13:42. Reason: fixed: missverständliche formulierung + smiley-syntax
                            Klingon function calls do not have “parameters”‒they have “arguments”‒and they always win them!

                            Comment


                            • #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.

                              Originally posted by 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…

                              Comment

                              Working...
                              X