Piet: Update eines arrays

Hallo,
mit folgenden Zeilen, möchte ich bestimmte Einträge in einem array korrigieren.
Die echo-Anweisung zeigt das korr. Element richtig an, im array ist es aber dann nicht korrigiert.
Was mache ich falsch?

foreach ($fields as $field)  
     {  
      if (......)  
         $field = $field . $zusatz;  
      echo $field . "<br>";  
     }  
  
print_r ($fields);  

  1. Was mache ich falsch?

    Du brauchst eine Referennz auf $filed.

    foreach ($fields as $field)

    {
          if (......)
             $field = $field . $zusatz;
          echo $field . "<br>";
         }

    print_r ($fields);

      
    ~~~php
      
    foreach ($fields as &$field){/* ... */}  
    
    
    1. Online Beispiel:
      http://codepad.org/K6GzcOaZ

    2. Du brauchst eine Referennz auf $filed.

      foreach ($fields as &$field){/* ... */}

      Danke, diese einfache Änderung funktioniert.  
        
      Jetzt aber ein weiteres Problem:  
      Neben der Änderung möchte ich abhängig von einer Bedingung auch Elemente aus dem array löschen. Gefunden habe ich unset.  
      Also bin ich munter ans Werk gegangen und habe geschrieben:  
      ~~~php
      foreach ($fields as &$field)  
           {  
            if (......)  
               $field = $field . $zusatz;  
            elseif (....)  
               unset($field);  
           }  
      print_r ($fields);  
      
      

      Der Eintrag ist aber leider nicht gelöscht.
      Woran liegt dies?
      Danke Euch
      Piet

      1. Woran liegt dies?

        unset() will wissen was es löschen soll. Mich wundert, dass Du keine Notizen empfängst. Auch wenn meine - flexible, lesbare, verständliche, funktionierende und nicht mit Nachteilen behaftete - Lösung wieder Widerspruch ernten wird: Hier die allfällige Erweiterung:

        <?php  
        $fields[1]='Hallo';  
        $fields[2]='weg damit';  
        $fields['foo']='Hallo';  
        $fields['bar']='Hallo';  
          
        foreach (array_keys($fields) as $key) {  
             if ('weg damit' == $fields[$key]) {  
                unset($fields[$key]);  
             } else {  
                 $fields[$key] .= ' Welt!';  
             }  
        }  
        print_r ($fields);  
        ?>
        

        ~> php test.php
        Array
        (
            [1] => Hallo Welt!
            [foo] => Hallo Welt!
            [bar] => Hallo Welt!
        )

        1. Variante nach dedlfix:

          <?php
          $fields[1]='Hallo';
          $fields[2]='weg damit';
          $fields['foo']='Hallo';
          $fields['bar']='Hallo';

          geänderte Zeile:

          foreach ($fields as $key => $value) {
                if ('weg damit' == $value) {
                   unset($fields[$key]);
                } else {
                    $fields[$key] .= ' Welt!';
                }
          }
          print_r ($fields);
          ?>

          ~> php test.php
          Array
          (
              [1] => Hallo Welt!
              [foo] => Hallo Welt!
              [bar] => Hallo Welt!
          )

        2. Tach!

          unset() will wissen was es löschen soll. Mich wundert, dass Du keine Notizen empfängst.

          Da kommt keine, weil es dazu keinen Grund gibt. Das unset($field) löscht $field, was in unserem Fall eine Referenz auf einen Array-Eintrag ist. Beim nächsten foreach-Durchlauf wird $field wieder als neue Referenz angelegt. Wenn nach dem unset() im restlichen Schleifenkörper noch Lesezugriffe auf $field kämen, dann wäre eine Notice gerechtfertigt.

          dedlfix.

        3. Auch wenn meine - flexible

          Auch meine Variante ist flexibel

          lesbare

          *schwerer lesbare

          verständliche

          und umständliche

          funktionierende

          da hab ich keine Einwand

          und nicht mit Nachteilen behaftete

          mal abgesehen von Laufzeit und Speicherbedarf

          • Lösung wieder Widerspruch ernten wird

          Und war das Genugtuung?

        4. Hi,
          Deinen Vorschlag

          <?php

          $fields[1]='Hallo';
          $fields[2]='weg damit';
          $fields['foo']='Hallo';
          $fields['bar']='Hallo';

          foreach (array_keys($fields) as $key) {
               if ('weg damit' == $fields[$key]) {
                  unset($fields[$key]);
               } else {
                   $fields[$key] .= ' Welt!';
               }
          }
          print_r ($fields);
          ?>

          
          >   
          
          konnte ich bei mir erfolgreich umsetzen.  
          Ob dies die beste Variante ist, kann ich nicht beurteilen.  
            
            
          Aber wie bekomme ich jetzt die Lücken raus?  
          Gibt es hierfür eine Funktion oder muss ich ein neues array erzeugen?  
          Gruß  
          Piet  
            
          
          
          1. Tach!

            Deinen Vorschlag
            konnte ich bei mir erfolgreich umsetzen.

            Welche Probleme hattest du denn mit den anderen Vorschlägen?

            Aber wie bekomme ich jetzt die Lücken raus?

            Welche Lücken? Hast du nummerierte Keys? Und wenn ja, warum brauchst du die lückenlos? Für das foreach jedenfalls nicht, das iteriert problemlos über alles möglichen Arrays, egal wie die Keys verteilt sind oder aussehen. Wie auch immer, es gibt array_values(), das erstellt ein neues Array nur aus den Werten und nummeriert die Keys von 0 an durch.

            dedlfix.

            1. Welche Probleme hattest du denn mit den anderen Vorschlägen?

              Ich habe nicht alle durchprobiert - aus Verständnisgründen.

              Aber wie bekomme ich jetzt die Lücken raus?

              Welche Lücken? Hast du nummerierte Keys? Und wenn ja, warum brauchst du die lückenlos?

              Wenn ich später über eine do-Schleife darüber gehe, kommt der Fehler, dass (sinngemäß): index x nicht existiert

              1. Wenn ich später über eine do-Schleife darüber gehe, kommt der Fehler, dass (sinngemäß): index x nicht existiert

                Dir fehlen offensichtlich Grundlagen zu Arrays und Konstrollstrukturen.

              2. Tach!

                Welche Probleme hattest du denn mit den anderen Vorschlägen?
                Ich habe nicht alle durchprobiert - aus Verständnisgründen.

                Dann solltest du dir unbedingt die von PHP vorgesehene und übliche Version

                foreach ($array as $key => $value)

                anschauen und in deinem Fall die mit der Referenz

                foreach ($array as $key => &$value)

                Aber wie bekomme ich jetzt die Lücken raus?
                Welche Lücken? Hast du nummerierte Keys? Und wenn ja, warum brauchst du die lückenlos?
                Wenn ich später über eine do-Schleife darüber gehe, kommt der Fehler, dass (sinngemäß): index x nicht existiert

                Warum willst du denn später mit do-while darübergehen? Was du vorhast, lässt sich sicher auch mit foreach erledigen, das sich an solchen Lücken nicht stört. Eine Abbruchbedingung kannst du auch innerhalb foreach ans Ende schreiben: if (bedingung) break;

                dedlfix.

                1. Hallo dedlfix,

                  Welche Probleme hattest du denn mit den anderen Vorschlägen?
                  Ich habe nicht alle durchprobiert - aus Verständnisgründen.

                  Dann solltest du dir unbedingt die von PHP vorgesehene und übliche Version

                  foreach ($array as $key => $value)

                  anschauen und in deinem Fall die mit der Referenz

                  foreach ($array as $key => &$value)

                  Mit der jetzt realisierten (umständlichen) Version kann ich einen Wert ändern oder löschen.
                  Die anderen Vorschläge beziehen sich, falls ich es richtig verstanden habe
                  entweder auf das Update:
                  foreach ($array as $key => &$value)
                  oder auf das Löschen:
                  foreach ($array as $key => &$value)
                  Wie ich beides in einer loop machen kann habe ich nicht gefunden (übersehen?)
                  Für einen entsprechenden Tipp wäre ich dankbar
                  Gruß
                  Piet

                  1. Wie ich beides in einer loop machen kann habe ich nicht gefunden (übersehen?)

                    Genau wie Freds Lösung auch. Hier ein Beispiel

                    1. Genau wie Freds Lösung auch. Hier ein Beispiel

                      Super, danke!

                  2. Tach!

                    Mit der jetzt realisierten (umständlichen) Version kann ich einen Wert ändern oder löschen.
                    Die anderen Vorschläge beziehen sich, falls ich es richtig verstanden habe
                    entweder auf das Update:
                    foreach ($array as $key => &$value)
                    oder auf das Löschen:
                    foreach ($array as $key => &$value)

                    Nochmal zum Verständnis der Problematik. Die einfachste Variante von foreach liefert dir nur den Wert des aktuellen Array-Element.

                    1a)  foreach ($array as $value)

                    In $value steht eine Kopie dieses Wertes. Deswegen nützt es dir nichts, wenn du diese Kopie änderst oder löschst, weil du darüber weder den eigentlichen Wert ändern noch das gesamte Element löschen kannst. Du benötigst für das eigentliche Element den Key, um über $array[$key] direkt den Wert ändern zu können oder auch das Element mit unset() zu entfernen. Deswegen gibt es die zweite foreach-Variante:

                    2a)  foreach ($array as $key => $value)

                    Diese liefert dir ebenfalls den Wert als Kopie und zusätzlich den Key. Damit hast du jetzt die Möglichkeiten über $value nur lesend auf den Wert zuzugreifen und über $array[$key] schreibend und löschen, aber auch lesend, wenn du willst. Doch für das Lesen ist jedoch $value einfacher als $array[$key] zu notieren.

                    Im Laufe der Zeit wurde in PHP dann noch die Variante mit der Referenz hinzugefügt.

                    1b)  foreach ($array as &$value)

                    Damit ist $value nun keine Kopie mehr sondern ein direkter Verweis auf den Wert. Darüber kannst du den Wert direkt mit einer Zuweisung an $value ändern. Aber du kannst damit nicht das Element löschen, weil du mit unset($value) nur $value selbst löschst, also nur den Verweis, aber nicht den eigentlichen Wert. Zum Löschen brauchst du immer noch den Key. Und dafür gibt es dann die Key-Value-Variante von foreach zusammen mit der Referenz-statt-Kopie-Schreibweise.

                    2b)  foreach ($array as $key => &$value)

                    Du kannst nun über $array[$key] lesend, schreibend und löschend zugreifen, über $value immerhin noch lesend und schreibend.

                    Wie ich beides in einer loop machen kann habe ich nicht gefunden (übersehen?)

                    Diese letzte Variante ist also das non-plus-ultra, aber mit Variante 2a kämest du auch schon ans Ziel.

                    Das sind die von PHP direkt vorgesehenene Wege, foreach auf ein Array anzuwenden. Da 1UnitedPower nicht wusste (und ich hätte es zunächst wohl auch nicht anders gemacht), dass du nicht nur schreibenden Zugriff benötigst, sondern auch löschen willst, war Variante 1b zunächst ausreichend, sonst wäre sicher gleich Variante 2a oder 2b ins Spiel gekommen. - Fred hat hingegen einen ungewöhnlichen Weg gewählt. Die Bedingung, um lesend, schreibend und löschend zugreifen zu können, ist ja der Key, und da die Funktion array_keys() alle Keys als (ein neues) Array liefert, nahm er diese Funktion. Die muss aber zunächst einmal selbst über das Array laufen, alle Keys einsammeln und sie in einem neuen Array ablegen. Dann läufst du mit foreach über dieses Array und über $array[$key] stehen dir alle Möglichkeiten offen. Es ist aber mit den direkten Varianten 2a und 2b nicht notwendig, zweimal über das Array zu laufen, weil diese gleich beim ersten/einzigen/eigentlichen Durchlauf alle Notwendige bereitstellen.

                    dedlfix.

                    1. Danke für diese Erläuterung!

      2. Tach!

        foreach ($fields as &$field){/* ... */}
                 unset($field);
        Der Eintrag ist aber leider nicht gelöscht.

        Damit löschst du nur die Referenz, nicht den eigentlichen Wert, auf den $field zeigt. Zum Löschen brauchst du den richtigen Eintrag im Array. Du kannst dafür die Variante

        foreach ($array as $key => $value)

        nehmen und dann

        unset($array[$key]);

        dedlfix.

  2. Moin!

    Was mache ich falsch?

    foreach ($fields as $field)

    {
          if (......)
             $field = $field . $zusatz;
          echo $field . "<br>";
         }

    print_r ($fields);

      
    Du änderst nicht den Array fields sondern nur die Variable field.  
      
    Du musst die Schlüssel des Arrays ermitteln und dann die Elemente damit adressieren:  
      
    "selbsterklärend:"  
      
    ~~~php
    <?php  
    $fields[1]='Hallo';  
    $fields[2]='Hallo';  
    $fields['foo']='Hallo';  
    $fields['bar']='Hallo';  
      
    $arKeys=[link:http://de3.php.net/manual/de/function.array-keys.php@title=array_keys]($fields);  
    foreach ($arKeys as $varKey) {  
         $fields[$varKey] .= ' Welt!';  
    }  
    print_r ($fields);  
    ?>
    

    ~> php test.php
    Array
    (
        [1] => Hallo Welt!
        [2] => Hallo Welt!
        [foo] => Hallo Welt!
        [bar] => Hallo Welt!
    )

    Eine Zeile kürzer:

    <?php  
    $fields[1]='Hallo';  
    $fields[2]='Hallo';  
    $fields['foo']='Hallo';  
    $fields['bar']='Hallo';  
      
    foreach (array_keys($fields) as $key) {  
         $fields[$key] .= ' Welt!';  
    }  
    print_r ($fields);  
    ?>  
    
    
    1. Tach!

      Du änderst nicht den Array fields sondern nur die Variable field.
      Du musst die Schlüssel des Arrays ermitteln und dann die Elemente damit adressieren:

      Ja. Nein, müssen muss er das nicht. Eine Lösung stand ja schon da: Referenzieren.

      Eine Zeile kürzer:
      foreach (array_keys($fields) as $key) {

      Selbst wenn man das über die Keys machen will, ist das zu umständlich. Es gibt

      foreach ($array as $key => $value)

      da wird der Key gleich mitgeliefert.

      dedlfix.

      1. Tach!

        Post!

        Selbst wenn man das über die Keys machen will, ist das zu umständlich. Es gibt

        foreach ($array as $key => $value)

        da wird der Key gleich mitgeliefert.

        Wow!

        Eine andere Zeile!

        foreach ($array as $key => $value) {
        statt:
        foreach (array_keys($array)as $key) {
        und dann auch noch statt dem wahnsinnig umständlichen
        $array[$key] .=' Welt';
        nur ein
        $array[$key] .=' Welt';

        Da wird der Interpreter aber vor Freude aus dem Häuschen sein, dass er es nicht so UMSTÄNDLICH vorgelegt bekommt.

        fred

        1. Tach!

          Selbst wenn man das über die Keys machen will, ist das zu umständlich. Es gibt
            foreach ($array as $key => $value)
          da wird der Key gleich mitgeliefert.
          Wow!
          Eine andere Zeile!

          Die "andere Zeile" bewirkt, dass nur einmal über das Array iteriert werden muss. Mit deiner Variante müssen zuerst alle Keys eingesammelt werden, um dann über sie zu laufen.

          und dann auch noch statt dem wahnsinnig umständlichen
          $array[$key] .=' Welt';
          nur ein
          $array[$key] .=' Welt';

          Zum Schreiben braucht man in beiden Varianten $array[$key], aber für das reine Lesen wäre $value vorhanden und einfacher handzuhaben. Damit fällt je Verwendung eine Auflösung des Ausdrucks $array[$key] und damit PHP-intern eine Suche im Array nach dem Schlüssel weg und der Leser hat es auch etwas einfacher.

          Zudem kann man diese Key-Value-Foreach-Schleife auch noch mit der Referenz kombinieren

          foreach ($array as $key => &$value)

          So kann man direkt über $value das Element lesen und schreiben und nur zum Löschen wird $array[$key] benötigt.

          Da wird der Interpreter aber vor Freude aus dem Häuschen sein, dass er es nicht so UMSTÄNDLICH vorgelegt bekommt.

          Dem wird das egal sein, die Laufzeitunterschiede sind sicher nicht spürbar. Es geht hier mehr um die Lesbarkeit. Der Funktionsaufruf ist unüblich, weil es eine direkte Lösung gibt. Das bremst vor allem beim Lesen des Codes.

          dedlfix.

    2. Du musst die Schlüssel des Arrays ermitteln und dann die Elemente damit adressieren

      Viel zu umständlich. Wozu gibts denn Referenzen?

      1. Moin!

        Du musst die Schlüssel des Arrays ermitteln und dann die Elemente damit adressieren

        Viel zu umständlich. Wozu gibts denn Referenzen?

        Um die Programme unverständlicher und fehleranfälliger zu machen.

        Dein Beispiel: http://codepad.org/deGcFVGR

        <?php  
          
        header("Content-Type: text/plain");  
          
        $fields = array();  
        $fields[] = "Foo";  
        $fields[] = "FooFoo";  
          
        foreach ($fields as &$field)  
        {  
              $field .= ' Bar';  
        }  
          
        print_r ($fields);  
          
        $field = "fail";  
          
        print_r($fields);
        

        - Sven Rautenberg

        1. Um die Programme unverständlicher und fehleranfälliger zu machen.

          Für diejenigen, die Referenzen nicht verstehen.

          Dein Beispiel: http://codepad.org/deGcFVGR

          Dein Beispiel: http://codepad.org/YdvmEIrj