Accéder à des index variables de tableaux multidimensionnels

Eléphant du PHP | 70 Messages

05 mai 2010, 04:27

Bonjour,

Je galère pas mal et malgré de nombreuses recherches, je ne vois pas comment accéder à des index variables de tableaux multidimensionnels.
Je m'explique

Code : Tout sélectionner

$tab = array( 'racine' => array( 'blocks' => array( 'bloc1' => array( 'content' =>'contenu du bloc 1', 'blocks' => array( (et encore plein d'imbrications...) ) ), 'bloc2' => array( 'content' =>'contenu du bloc 2', 'blocks' => array( (et encore plein d'imbrications...) ) ) ) ) )
Je connais le chemin pour parcourir les blocs.
Je sais que si je veux récupérer le code du bloc 1, je n'ai qu'à faire

Code : Tout sélectionner

$tab['racine']['blocks]['bloc1']
Mais je ne peux pas le coder en dur.

Ma manière d'accéder à l'élément serait simple si on pouvait procéder de la manière suivante

Code : Tout sélectionner

function chercherInfo($tab, 'racine.blocks.bloc1')
Code pour accéder à une profondeur de tableau de 1

Code : Tout sélectionner

$path = 'blocks' ; $tab[$path] ;
Pseudo-code pour accéder à une profondeur de tableau de 2 ?

Code : Tout sélectionner

$path = 'blocks][bloc1' ; $tab[$path] ;
La syntaxe est horrible et c'est évident que ça ne peut marcher, mais ça devrait vous aider à comprendre mon besoin.
Si vous avez une manière alternative simple, efficace et élégante pour avoir un accès de ce type, quitte à passer par de l'objet avec la SPL (j'ai pas encore bien saisi la manière dont ArrayAccess fonctionne, si ça se trouve vous pourrez m'apprendre que ça serait ma solution ou au contraire une voie sans issue). Après SimpleXML, SimpleArray ? :priere:

Actuellement je me débrouille en faisant cela par fichiers, mais je trouve que ce n'est tout de même pas très optimisé.

Merci pour avoir pris le temps de lire ! (et éventuellement, celui de répondre :))
Comme dit un ami
"Il n’y a jamais de bugs dans les programmes que j’écris : juste des caractéristiques non documentées"

ViPHP
ViPHP | 2287 Messages

05 mai 2010, 09:46

Hello,
Ma manière d'accéder à l'élément serait simple si on pouvait procéder de la manière suivante

Code : Tout sélectionner

function chercherInfo($tab, 'racine.blocks.bloc1')
A priori cela est faisable tel quel (avec ou sans, suivant les goûts, l'aide d'une petite fonction récursive qui te permettrait de gérer un nombre de niveaux virtuellement illimité) :) Sous quelle version de php tournes-tu ?

Le problème est assez simple comme tu l'as bien compris : quand on veut accéder à un index dans un tableau, on écrit :
$var= $tableau['cle_1']['cle_2']['cle_3'] /* ...  */ ['cle_n'];
Dans cette écriture on peut facilement rendre variables les index... Mais pas les crochets (qui symbolisent l'accès aux index). Ce qui fait que l'expression sera toujours figée dans le nombre n de niveaux traversés (même si on transforme cette ligne en mettant un maximum de variables, le nombre de crochets, lui, ne va pas varier).

Maintenant qu'on sait ce qui gêne, essayons d'imaginer ta fonction chercherInfo :
function chercherInfo($tableau, $chemin){
  $chemin_eclate=explode('.',$chemin);
  /*
   * Ici la partie intéressante qui manque
   */
  return $valeur;
}
Reste plus qu'à savoir avec quoi on va remplacer le commentaire du milieu. Je te donne trois pistes pour ça (je te laisse écrire ces codes toi-même, car l'exercice est amusant, et je l'espère à ta portée avec mes indications):

- La première piste fait appel à eval() (oui, la fonction dont le nom rime avec "c'est mal") mais elle a l'avantage d'être super simple (et presque justifiée car tu veux là outrepasser une limite syntaxique de php). Elle consiste à construire dans une chaîne de caractères (donc de manière 100% variable) la fameuse ligne de code et de passer tout ça dans eval(). Et hop ça marche (en une seule ligne) ;)

- La deuxième piste, plus compliquée (mais aussi plus propre et probablement plus performante), utilise la récursion : notre fonction chercheInfo s'appellera elle-même, autant de fois qu'il le faudra, et va accéder à un seul niveau du tableau à la fois. Quand on arrive au "dernier" niveau, on arrête cette chaîne d'appels et on retourne simplement la valeur obtenue, et on se retrouve alors dans l'appel précédent de chercheInfo qui lui-même retourne bêtement la valeur que tu viens de récupérer, passant ainsi à l'appel précédent... et ainsi de suite. (pas de panique : heureusement c'est plus simple à faire qu'à expliquer ! ;) )

- Il y a aussi un compromis intéressant, utilisant une simple boucle (for ou foreach au choix) à la place de la récursion (mais utilisant le même principe d'accès à un niveau à la fois). C'est à priori la méthode la plus avantageuse au niveau perfs d'ailleurs et vraiment simple à écrire (tellement simple que j'y pense en dernier, lol)

A toi maintenant :D
if(!@work()){ Nespresso(); } else { what(); }
______________________________

Eléphant du PHP | 70 Messages

05 mai 2010, 11:44

Hmmm oui, je retrouve ce que j'avais pensé faire, néanmoins ça bloque dès qu'il s'agit de rajouter des infos...
PHP : 5.2 (et malheureusement pas PHP 5.3)

1- Eval() fonction que j'exclus d'emblée, ça doit être désactivé sur la plupart des hébergeurs mutualisés vu sa dangerosité...

2- facile, mais je passe directement à la 3

Code : Tout sélectionner

function parseTab($tab,$fullpath){ $tab_index = explode('.',$fullpath) ; foreach($tab_index as $I){ if(!isset($tab[$I])) return false ; $tab = $tab[$I] ; } return $tab ; }
Mon soucis, c'est si je veux rajouter un élément 'bloc21' par exemple (accessible par racine.blocks.bloc2.blocks.bloc21)
Le niveau de profondeur varie, donc comme tu l'as souligné je ne peux pas utiliser les '][' codés en dur.

J'ai eu une révélation cette nuit et ai tâché de "filouter" en prenant le chemin à l'envers pour reconstruire l'arborescence

$newtab = array('block21' => array('mon nouveau contenu') );
$newtab = array('blocks' => $newtab );
$newtab = array('block2' => $newtab );
...

Et là, un array_merge_recursive ou un foreach semblent pouvoir répondre à mes besoins, je donnerai le code final quand il sera prêt ;)
edit :

Code : Tout sélectionner

function setTab($newtabcontent,$fullpath){ $tab_index = explode('.',$fullpath) ; $tab_index = array_reverse($tab_index); foreach($tab_index as $I){ $newtabcontent = array($I => $newtabcontent) ; } return $newtabcontent ; } $content = 'plop' ; $tmp_tab = setTab($content,'content.blocs.news2.blocs.news21.blocs.news213.html'); $final = array_merge_recursive($tmp_tab,$tab1);
Comme dit un ami
"Il n’y a jamais de bugs dans les programmes que j’écris : juste des caractéristiques non documentées"

Eléphant du PHP | 70 Messages

10 mai 2010, 11:48

En fait non, le array_merge_recursive ne remplace pas les valeurs mais les ajoute.
PHP 5.3 apporte la nouveauté (et ma réponse) avec http://www.php.net/manual/fr/function.a ... ursive.php

Voilà qui devrait simplifier le travail, et pour ceux qui n'auraient pas encore PHP >= 5.3, dans les commentaires de la fonction on trouve ceci qui semble faire très bien le job...
Je teste pour vous ! :)
if (!function_exists('array_replace_recursive'))
{
  function array_replace_recursive($array, $array1)
  {
    function recurse($array, $array1)
    {
      foreach ($array1 as $key => $value)
      {
        // create new key in $array, if it is empty or not an array
        if (!isset($array[$key]) || (isset($array[$key]) && !is_array($array[$key])))
        {
          $array[$key] = array();
        }
 
        // overwrite the value in the base array
        if (is_array($value))
        {
          $value = recurse($array[$key], $value);
        }
        $array[$key] = $value;
      }
      return $array;
    }
 
    // handle the arguments, merge one by one
    $args = func_get_args();
    $array = $args[0];
    if (!is_array($array))
    {
      return $array;
    }
    for ($i = 1; $i < count($args); $i++)
    {
      if (is_array($args[$i]))
      {
        $array = recurse($array, $args[$i]);
      }
    }
    return $array;
  }
}
Comme dit un ami
"Il n’y a jamais de bugs dans les programmes que j’écris : juste des caractéristiques non documentées"

Eléphant du PHP | 217 Messages

10 mai 2010, 13:59

Bonjour,
pour gérer la récursivité la spl dispose de tout ce qu'il te faut : RecursiveIteratorIterator et RecursiveArrayIterator.

Un petit exemple :
$tab = array(
       'racine' => array(
              'blocks' => array(
                      'bloc1' => array(
                              'content' =>'contenu du bloc 1',
                              'blocks' => array(
                                  'block11' => 'contenu block 11'
                                  ,'block12' => 'contenu block 12'         
                              )
                     ),
                      'bloc2' => array(
                             'content' =>'contenu du bloc 2',
                              'blocks' => array(
                                   'block21' => 'contenu block 21'
                                  ,'block22' => 'contenu block 22'      
                               )
                     )
              )
       )
);

function search($key, $tab)
{
	$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($tab));
	foreach($it as $cle => $val)
	{

		if($cle == $key)
		{
			return $val;
		}
	}
}

echo search('block22', $tab);