PHP/Tutorials/Arrays mal anders herum

Aus SELFHTML-Wiki
< PHP‎ | Tutorials
Wechseln zu: Navigation, Suche

Informationen zum Autor

Name:
Thomas Schmieder
E-Mail:
Homepage:
bitworks.de

Im SelfHTML-Forum wird immer wieder danach gefragt, wie man diverse unterschiedliche Arrays sortiert, wie man sie zum Speichern von Daten/Datensätzen verwendet, wie man doppelte Einträge vermeidet, wie man einen bestimmten Wert oder Schlüssel in einer tiefen Struktur findet u.v.m.

Dieser Artikel soll dazu anregen es einfach mal „anders herum“ anzufangen, meine Anregungen aus den vielen Threads des Forumsarchivs zu sammeln, neu zu strukturieren und damit allen aufgeschlossenen Programmierern verständlich zugänglich machen.

Nützliche Funktionen

Die folgenden Funktionen stellen Denkansätze dar und sollten nach Belieben weiterentwickelt werden.

  • vier universelle Bearbeitungsfunktionen für Spaltenarrays
  • Beispiele zum Umgang mit Spaltenarrays
  • Linearisierung einer Arraystruktur (Erzeugung einer Pfadliste)
  • Bildung tiefer Arraystrukturen aus einer Pfad-Liste

Bearbeitungsfunktionen

Get-Record-Funktion (ähnlich SELECT)

Beispiel
function get_record (&$_data, $recno)
{
    $_rec = array();

    foreach ($_data as $colname => $field)
    {
        $_rec[$colname] = isset($_data[$colname][$recno])?$_data[$colname][$recno]:NULL;
    }

    return $_rec;
}
Die Funktion übernimmt als Argumente
  • &$_data eine Referenz auf das Daten-Array
  • $recno die Datensatznummer (Primary Key)

In der foreach()-Schleife werden die vorhandenen Spalten gesucht und dann festgestellt, ob es einen Feldwert zur Datensatznummer gibt. Die Treffer werden im Ergebnisarray $_rec gesammelt.

Für den Fall, dass es in einer Spalte keinen Eintrag für den Schlüssel $recno gibt, wird im Ergebnisdatensatz NULL als Wert im zugehörigen Datenfeld eingetragen.

Delete-Record-Funktion (ähnlich DELETE)

Beispiel: function delete_record (&$_data, $recno)
function delete_record (&$_data, $recno)
{
    $cols_deleted = 0;

    foreach ($_data as $colname => $field)
    {
        unset($_data[$colname][$recno]);
        $cols_deleted++;
    }

    return $cols_deleted;
}
Die Funktion übernimmt als Argumente
  • &$_data eine Referenz auf das Daten-Array
  • $recno die Datensatznummer (Primary Key)

In der foreach()-Schleife werden die vorhandenen Spalten gesucht und dann die Feldwerte zur Datensatznummer gelöscht. Die Funktion unset() liefert keinen Fehler, wenn das geforderte Element nicht existiert hat, ein if (isset(...)) ist daher nicht notwendig.

Der Rückgabewert zeigt hier lediglich die Anzahl der gefundenen Spalten an. Diese hat hier aber keinen Aussagewert darüber, ob die Spalte in diesem Datensatz auch belegt war.

Update-Record-Funktion (ähnlich UPDATE)

Beispiel
function update_record(&$_data, $recno, $_record, $expand=false)
{
    $cols_updated = 0;

    foreach ($_record as $colname => $data)
    {
        if ($expand or isset($_data[$colname][$recno]))
        {
            $_data[$colname][$recno] = $data;
            $cols_updated++;
        }
    }

    return $cols_updated;
}

Insert-Record-Funktion (ähnlich INSERT, REPLACE, ALTER)

Beispiel
function insert_record(&$_data, $recno, $_record, $expand=false, $overwrite=false)
{
    $cols_inserted = 0;

    if (!$overwrite)
    {
        foreach($_record as $colname => $data)
        {
            if (isset($_data[$colname][$recno]))
            {
                return 0;
            }
        }
    }

    foreach ($_record as $colname => $data)
    {
        if ($expand or isset($_data[$colname]))
        {
            $_data[$colname][$recno] = $data;
            $cols_inserted++;
        }
    }

    return $cols_inserted;
}
Die Funktion übernimmt in $_record ein Array mit einem Datensatz und trägt diesen unter der $recno in das Array $_data ein. Bleibt $overwrite==false, wird der Eintrag nur dann vorgenommen, wenn noch kein Index mit dem Wert $recno im Array $_data vorhanden ist. Setzt man $expand==true, werden diejenigen Spalten, die im Datensatz zusätzlich vorhanden sind, in $_data angelegt.

Zeilenarray zum Spaltenarray umbauen

Beispiel
$_spaltenarr = array();

foreach ($_zeilenarr as $key => $_record)
{
    insert_record($_spaltenarr, $key, $_record, true)
}
Erläuterungen


Diverse Funktionen

Musterdaten

Beispiel
<?php  ### musterdaten.php ### utf-8 ### ÄÖÜäöü

$_sparr = array();
$_sparr['vorname'] = array();
$_sparr['name'] = array();
$_sparr['strasse'] = array();
$_sparr['ort'] = array();
$_sparr['date'] = array();

$_sparr['vorname'][1] = 'Thomas';
$_sparr['name'][1] = 'Tollwut';
$_sparr['strasse'][1] = 'Tollkirschenweg';
$_sparr['ort'][1] = 'Torberg';

$_sparr['vorname'][2] = 'Michaela';
$_sparr['name'][2] = 'Mustermann';
$_sparr['strasse'][2] = 'Moorhuhnsiedlung';
$_sparr['ort'][2] = 'München';
$_sparr['telefon'[2] = '080/555333-0';

$_sparr['vorname'][3] = 'Paula';
$_sparr['name'][3] = 'Pingel';
$_sparr['strasse'][3] = 'Prachtallee';
$_sparr['ort'][3] = 'Petersburg';

$_sparr['vorname'][4] = 'Karel';
$_sparr['name'][4] = 'Gott';
$_sparr['strasse'][4] = 'An der Stadthalle';
$_sparr['ort'][4] = 'Prag';

$_sparr['vorname'][5] = 'Schlomo';
$_sparr['name'][5] = 'Freud';
$_sparr['strasse'][5] = 'Universitätspark';
$_sparr['ort'][5] = 'Wien';
$_sparr['telefon'][5] = '0815/4711-110';

?>

Spaltenarray zum Zeilenarray umbauen

Beispiel
<?php  ### col2row.php ### utf-8 ### ÄÖÜäöü

header('content-type: text/html; Charset=utf-8');
include musterdaten.php;

#===============================================================================
function array_col2row($_data)
{
    $_rows = Array();

    foreach ($_data as $col => $_values)
    {
        if (is_array($_values))
	{
            foreach($_values as $key => $param)
	    {
		$_rows[$key][$col] = $param;
	    }
	}
    }
	
    return $_rows;
}

#===============================================================================

echo "<pre>\r\n";
echo htmlspecialchars(print_r(array_col2row($_sparr),1)) . "\r\n";
echo "</pre>\r\n";	

?>


Sortierte Ausgabe

Sortier-Typen laut PHP-Handbuch

  • SORT_REGULAR - vergleiche Einträge normal (ohne die Typen zu ändern)
  • SORT_NUMERIC - vergleiche Einträge numerisch
  • SORT_STRING - vergleiche Einträge als Strings
  • SORT_LOCALE_STRING - vergleiche Einträge als Strings, basierend auf den aktuellen Locale-Einstellungen. Wurde in PHP 4.4.0 und 5.0.2 hinzugefügt. Es wird die System-Locale benutzt, die mittels setlocale() geändert werden kann.
  • SORT_NATURAL - Einträge in natürlicher Ordnung sortieren, wie bei natsort()
  • SORT_FLAG_CASE - Kann kombiniert werden (|, bitweises ODER) mit SORT_STRING oder SORT_NATURAL um Strings ohne Beachtung von Versalien und Gemeinen zu sortieren.
Beispiel
<?php  ### sorted_output.php ### utf-8 ### ÄÖÜäöü

header('content-type: text/html; Charset=utf-8');
include musterdaten.php;

echo "<pre>\r\n";

asort($_sparr['name'],SORT_NATURAL);
asort($_sparr['ort'],SORT_NATURAL);
asort($_sparr['strasse'],SORT_NATURAL);
asort($_sparr['vorname'],SORT_NATURAL);


echo "-------------------------------------\r\n";
echo "Ausgabe sortiert nach [name]:\r\n";
foreach($_sparr['name'] as $key => $param)
{
    echo "\r\nSatz Nr $key:\r\n";
    foreach($_sparr as $name => $_col)
    {
        if (isset($_sparr[$name][$key]))
            echo htmlspecialchars("[$name] => {$_sparr[$name][$key]}") . "\r\n";
    }
}

echo "-------------------------------------\r\n";
echo "Ausgabe sortiert nach [vorname]:\r\n";
foreach($_sparr['vorname'] as $key => $param)
{
    echo "\r\nSatz Nr $key:\r\n";
    foreach($_sparr as $name => $_col)
    {
        if (isset($_sparr[$name][$key]))
            echo htmlspecialchars("[$name] => {$_sparr[$name][$key]}") . "\r\n";
    }
}

echo "-------------------------------------\r\n";
echo "Ausgabe sortiert nach [ort]:\r\n";
foreach($_sparr['ort'] as $key => $param)
{
    echo "\r\nSatz Nr $key:\r\n";
    foreach($_sparr as $name => $_col)
    {
        if (isset($_sparr[$name][$key]))
            echo htmlspecialchars("[$name] => {$_sparr[$name][$key]}") . "\r\n";
    }
}

echo "</pre>\r\n";	

?>

Funktion für die einfache Sortierung

Beispiel
<?php   ### array_get_sorted.php ### utf-8 ### ÄÖÜäöü

#===============================================================================
function array_get_sortet(&$_data, $sortkey, $sorttype = SORT_NATURAL)
{
    if (!isset($_data[$sortkey])) return false;
    asort($_data[$sortkey], $sorttype);
    $_sortet = false;
	
    foreach($_data[$sortkey] as $key => $param)
    {
	foreach($_data as $name => $_col)
	{
	    $_rec[$name] = (isset($_data[$name][$key])?$_data[$name][$key]:NULL);
	}
	$_sortet[$key] = $_rec;
    }
	
    return $_sortet; 
}

#===============================================================================
# php unit test
#===============================================================================

header('content-type: text/html; Charset=utf-8');

include musterdaten.php;

echo "<pre>\r\n";
echo htmlspecialchars(print_r(array_get_sortet($_sparr, 'telefon'),1));
echo "</pre>\r\n";	


?>


Ergebnis

Beispiel
Array
(
    [2] => Array
    (
        [vorname] => Michaela
        [name] => Mustermann
        [strasse] => Moorhuhnsiedlung
        [ort] => München
        [telefon] => 080/555333-0
        [date] => 
    )

    [5] => Array
    (
        [vorname] => Schlomo
        [name] => Freud
        [strasse] => Universitätspark
        [ort] => Wien
        [telefon] => 0815/4711-110
        [date] => 
    )
)
Führt man mit der Funktion array_get_sorted() eine Sortierung über eine Spalte durch, werden in das Ergebnisarray nur diejenigen Datensätze aufgenommen, die auch ein Element mit dem Bezeichner (Sortierspalte) enthalten. Einen Wert muss dieses Element nicht enthalten. Sortiert wird hier die angebene Spalte des Originalarrays, da es als Referenz an die Funktion übergeben wird. Wenn man das nicht will, muss man die Referenzanweisung & aus dem Funktionskopf entfernen.

Linearisierung tiefer Arrays

Manchmal ist es notwendig, alle Elemente tiefer Baumstrukturen zu kennen. Hierzu kann man die Pfade zu den Elementen auflösen und jeweils als einzeiligen String darstellen. Jedes Element des Baumes bekommt damit eine Zeile in einer Liste.

Beispiel
#------------------------------------------------------------------------------
    function linearize($_source, &$_target, $path='')
    {
        if (is_array($_source))
        {
            foreach($_source  as $key => $val)
            {
                linearize($val, &$_target, $path.'.'.$key);
            }    
        }
        else
        {
            $_target[] = ltrim($path,'.');
        }
    }
#------------------------------------------------------------------------------

Aus dem Beispielarray

Beispiel
    $_testarr = array();

    $_testarr['vorname'][0] = 'Hans';
    $_testarr['vorname'][1] = 'Klaus';
    $_testarr['vorname'][2] = 'Peter';
    $_testarr['name']       = 'Mustermann';
    $_testarr['plz']        = '37444';
    $_testarr['interessen']['privat'][0] = 'lesen';
    $_testarr['interessen']['privat'][1] = 'Fußball';
    $_testarr['interessen']['beruflich'] = 'PHP';

wird durch Anwendung der obigen Funktion die folgende Liste:

    [0] => vorname.0
    [1] => vorname.1
    [2] => vorname.2
    [3] => name
    [4] => plz
    [5] => interessen.privat.0
    [6] => interessen.privat.1
    [7] => interessen.beruflich

die wiederum als Array (jedoch nur als "eindimensionales") gespeichert wird.

Selbstverständlich kann man einen anderen "Trenner" benutzten und auch die Werte mit in die Liste aufnehmen. Der Fantasie sind da keine Grenzen gesetzt. Wichtig ist dabei nur, dass der Trenner nicht in den ursprünglichen Bezeichnern vorkommen darf und dass die Werte keine schädlichen Zeichen mehr enthalten. Diese Regeln gelten in ähnlicher Form bei CSV-Dateien.

Tiefes Array (Baumstruktur) aus Pfadliste erstellen

Aus einer solchen Zeile der Pfadliste kann mit mit der folgenden Funktion wieder ein tiefes Array erstellen. Durch eine Schleife über die Liste und Addition der tiefen Arrays kann das Gesamtarray wieder erzeugt werden.

Beispiel
#------------------------------------------------------------------------------
    function make_deep_array($array_pathstr, $value=NULL, $sep='.')
    {
        $_arr = array();
        $array_pathstr = trim($array_pathstr, $sep);
        $_ref =& $_arr;

        while (strlen($array_pathstr) > 0)
        {
            $path = strtok($array_pathstr, $sep); 
            $array_pathstr = substr($array_pathstr,strlen($path)+1);
            $_ref[$path] = NULL;
            $_ref =& $_ref[$path];
        }
     
        $_ref = $value;
     
        return $_arr;
    }
#------------------------------------------------------------------------------

Mit ein wenig Überlegung kann man sich die Funktionen selbstverständlich auch so umbauen, dass nicht nur die Pfade, sondern auch die Werte mit berücksichtigt werden.

Die "Name-Value"-Pärchen kann man dann auch bequem in einer Datei abspeichern, und so eine incrementelle Serialisierung von Arrays realisieren. Im Gegensatz zu PHPs mit serialize()/unserialize() erzeugten Strukturen, kann man eine derartige Liste auch partiell einlesen und verarbeiten. Die Listen unterliegen aber auch festen Regeln, die man streng beachten muss, wenn man keine Datenverluste erleiden will.

Siehe auch