PHP-Scripte PHP-Tutorials PHP-Jobs und vieles mehr

PHP-Scripte PHP-Tutorials PHP-Jobs und vieles mehr (https://www.php-resource.de/forum/)
-   PHP Developer Forum (https://www.php-resource.de/forum/php-developer-forum/)
-   -   mehrdimensionales Array durchduchen (https://www.php-resource.de/forum/php-developer-forum/104892-mehrdimensionales-array-durchduchen.html)

wspl 06-01-2015 01:03

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

h3ll 06-01-2015 05:42

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

combie 06-01-2015 10:21

Zitat:

Zitat von wspl (Beitrag 670242)
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()

fireweasel 06-01-2015 14:27

Zitat:

Zitat von combie (Beitrag 670244)
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:

Zitat von wspl (Beitrag 670242)
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. ;)

combie 06-01-2015 14:37

Zitat:

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.

fireweasel 06-01-2015 15:24

Zitat:

Zitat von combie (Beitrag 670247)
PHP-Code:

$fahrten array_filter($fahrten, .....); 

Für mich will er in der Code Spalte suchen...

Zitat:

Zitat von wspl (Beitrag 670242)
(Anschließend soll diese Zeile gelöscht werden, ...

Zitat:

(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", ...).

Zitat:

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()?

combie 06-01-2015 18:01

Zitat:

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


Zitat:

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.

wspl 06-01-2015 18:08

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

combie 06-01-2015 18:14

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);


fireweasel 06-01-2015 19:32

Zitat:

Zitat:

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:

Zitat von combie (Beitrag 670251)
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.

Zitat:

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.

combie 06-01-2015 23:15

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

Ich hoffe, dich stört es nicht, wenn mich dieses langweilt ....

mermshaus 07-01-2015 07:40

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:

Zitat von wspl (Beitrag 670250)
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]);



combie 07-01-2015 13:10

Zitat:

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

Zitat:

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


Zitat:

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

Zitat:

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 :huep:

fireweasel 07-01-2015 15:11

Zitat:

Öhmmm...
Mich deucht, du übertreibst....
Mir deucht, du überschätztest das Ausmaß meiner Übertreibung.
Ich wollte nur ein wenig spielen. ;)
Zitat:

Errege dich, wenn du magst....
Nicht "erregt", nur ein wenig verwundert. Ich empfand deine bisherigen Beiträge immer als lehrreich.
Zitat:

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:

Zitat von combie (Beitrag 670255)
Foreach arbeitet (intern) auch mit einer Kopie des Arrays. :cool:
(soweit mir bekannt)

Ähmm, nein ... es erzeugt lediglich eine Kopie des Array-"Zeigers", der Rest wird referenziert. (soweit mir bekannt)

Zitat:

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.

Zitat:

... :rocks:
Am Speichermanagement von PHP rockt eher wenig. :p

Zitat:

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*

Zitat:

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

mermshaus 08-01-2015 06:06

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:

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.

Zitat:

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


Alle Zeitangaben in WEZ +2. Es ist jetzt 11:26 Uhr.

Powered by vBulletin® Version 3.8.2 (Deutsch)
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.0
[c] ebiz-consult GmbH & Co. KG