web-dev-qa-db-fra.com

Comment autoriser les clés en double dans un tableau php

Comment autoriser un tableau php à avoir des clés dupliquées? Lorsque j'essaie d'insérer une clé, la paire valeur/clé déjà existante écrase la valeur de la clé précédente correspondante avec la nouvelle valeur. Existe-t-il un moyen de conserver les deux clés en double ayant des valeurs différentes?

36
sudh

Vous pouvez avoir une seule clé qui a la valeur d'un tableau (ou tableau multidimensionnel), qui contiendrait tous les éléments avec cette clé donnée. Un exemple pourrait être

$countries = array(
  "United States" => array("California", "Texas"),
  "Canada" => array("Ontario", "Quebec")
);
55
Mike Lewis
$array[$key][] = $value;

Vous y accédez ensuite via:

echo $array[$key][0];
echo $array[$key][1];

Etc.

Notez que vous créez un tableau de tableaux à l'aide de cette méthode.

29
Matthew

Tout l'intérêt du tableau est d'avoir des clés uniques. Si vous souhaitez stocker des paires de valeurs, alors:

$array[] = [$value1, $value2];

Si vous avez beaucoup de dupes, cette alternative sera plus efficace:

<?php

if (array_key_exists($key, $array)) 
    $array[$key]['occurrences']++; 
else 
    $array[$key] = ['value'=>$value, 'occurrences'=>1];
13
Kornel

PHP ne permet pas cela. La meilleure solution consiste à utiliser un tableau multidimensionnel. Par exemple...

<?php

    $mArray = array(array("key1" => "value1"),
                    array("key2" => "value2"),
                    array("key3" => "value3"),
                    array("key1" => "value4"));

?>

Remarquez comment j'ai des clés en double nommées key1.

Maintenant, si je veux appeler chaque instance de key1, lancez

<?php

    $desiredKeyName = "key1";

    foreach ($mArray as $aValue) {

        foreach ($aValue as $key => $value) {

            if ($key == $desiredKeyName) {

                echo $value . "<br />";
            }
        }
    }

?>

et il reviendra

value1
value4
6
Steve Robbins

Je vous présente: Archive Array

Utilisation de l'échantillon.

<?php
$Arch = new archiveArray(); //Class setup

// Set and overwrite the data few times
$Arch -> data = 'one';
$Arch -> data = 2;
$Arch -> data = 'tree XD';

// Get the latest data, as per expected behaviour of an array object
var_dump( $Arch -> data ); // 'tree XD'

// Get its previously set archived values
var_dump( $Arch -> getArchived( 'data' ) ); // ['one', 2]
?>

Code de la classe

<?php
///
/// An object array, which keeps an archived copy 
/// of all its previously set values. 
///
/// @author [email protected]
///
class archiveArray {

    public $Arch_data = array();
    public $Arch_archive = array();

    public function archiveArray() {
        $Arch_data = array();
        $Arch_archive = array();
    }

    public function setData($name, $value) {
        if( array_key_exists( $name, $this -> Arch_data ) ) {

            if( !array_key_exists( $name, $this -> Arch_archive ) ) {
                $this -> Arch_archive[ $name ] = array();
            } else {
                if( !is_array($this -> Arch_archive[ $name ] ) ) {
                    $this -> Arch_archive[ $name ] = array();
                }
            }

            array_Push( $this -> Arch_archive[ $name ] , $this -> Arch_data[ $name ] );

        }

        $this -> Arch_data[ $name ] = $value;
    }

    public function getData($name) {
        return $this -> Arch_data[ $name ];
    }

    public function getArchived($name) {
        if( array_key_exists( $name, $this -> Arch_archive ) ) {
            return $this -> Arch_archive[ $name ];
        }
        return null;
    }

    //!!!--- OVERLOAD functionalities START ---!!!//
    public function __set($name, $value) {      //Uses the 'set' to create a node in the default type setting
        $this -> setData($name, $value);
    }

    public function __get($name) {
        return $this -> getData($name);
    }
    //!!!--- OVERLOAD functionalities END ---!!!//
}
?>

TLDR: Parfois, vous avez besoin d'un bidouillage comme celui-ci pour faire le travail rapidement!

Sa question peut avoir une forte controverse et va à l'encontre des enseignements de l'informatique. (avant de tirer, lisez le tout) Mais il y a des cas où vous voulez que cela se produise. = X

Par exemple, vous avez une base de code, qui manipule un ensemble spécifié d’objets de tableau. Et en raison de son utilisation répétée (boucles?, Récursives?). Il remplace ou redéfinit le résultat. Jusqu'à la dernière série est donnée. 

Et quand tout est fini. Vous réalisez soudainement que vos spécifications client (ou vos) ont changé. Au lieu des données finales, vous voulez chaque donnée entre les deux (donc vous voulez plus d'une donnée par clé). Et dans le cas malheureux, votre système était déjà terminé de manière si compliquée qu'il est difficile de changer tout pour pouvoir travailler facilement avec un tableau multi-dimensionnel (ce qui signifie que remplacer ne fonctionnerait pas, surtout si vous utilisez appels dynamiques). Alors tu fais quoi?

C’était en fait un scénario que j’ai rencontré récemment, mais il existe un hack simple qui garantit que tout votre code fonctionne toujours, tout en conservant les anciennes données. 

Le résultat final, une classe qui peut toujours être traitée comme n'importe quel autre objet. Mais a gagner une capacité d'archivage, pour conserver les anciennes données. C'est un tableau multi-dimensionnel, avec l'index [0] accédé directement. Et cela fonctionne simplement en changeant la déclaration de variable avec cet objet. Et toutes les modifications apportées au paramètre d'objet seraient archivées. Pour un accès facile, avec peu ou pas de changement dans le programme de code complet =)

5
PicoCreator

Je suis venu avec une solution simple tout en travaillant sur un projet personnel.

Puisque je voulais une sorte de clé dupliquée, j'ai décidé de stocker ma clé de tableau => valeurs dans un ordre inverse valeur => clé où valeur devient la clé et la clé devient la valeur, de cette façon, je pourrais avoir des clés dupliquées qui sont en fait valeurs. Je ne crée pas de valeurs dupliquées afin que cela fonctionne dans ce cas particulier.

Donc, un petit exemple:

$r = array ( 'banana'=>'FRUIT', 'Apple'=>'FRUIT', 'broccoli'=>'VEG', 'peas'=>'VEG' );

function get_food_group ( $type, $bowl ) {
    return array_keys ( $bowl, $type );
}

print_r ( get_food_group('FRUIT', $r) );

# PRINTS #
# Array
# (
#    [0] => banana
#    [1] => Apple
# )

Si vous voulez avoir quelque chose comme:

array (
    'banana' => 'FRUIT',
    'Peach' => 'FRUIT',
    'banana' => 'YELLOW'
)

Ensuite, j'irais avec une autre solution.

4
darth lemon

Ce n'est pas tellement que "vous ne pouvez pas le faire". Les inconvénients d'un tableau avec des clés dupliquées deviennent évidents lorsque vous avez réellement essayé de l'utiliser.

  • Vous perdez la capacité d'adresser le contenu individuellement. Pour les accès $array['duplicate'], vous ne verrez jamais que la première entrée.
  • Donc, pratiquement, vous ne pouvez utiliser un tel objet que dans une foreach qui voit chaque paire clé/valeur indépendamment de l’ambiguïté.
  • Voir ci-dessous, vous devez également décider comment gérer les tentatives non définies, ou si les entrées peuvent être écrasées. Un mode avec ajout uniquement est le plus facile à mettre en œuvre. (Et c’est le cas d’Egde où cela pourrait avoir un sens.)

Quoi qu'il en soit, pour avoir aussi une réponse textuelle à la question: vous pouvez utiliser la syntaxe de tableau PHP mais avoir un objet d'accumulation à la place avec:

class DuplicateArray implements ArrayAccess, Iterator, Countable {

    var $keys = array(),
        $values = array();
    var $pointer = 0;

    // initialize from array
    function __construct($from=array()) {
        $this->keys = array_keys($from);
        $this->values = array_values($from);
    }

    // iteration
    function count() {
        return count($this->keys); 
    }
    function current() {
        return $this->values[$this->position];
    }
    function key() {
        return $this->keys[$this->position];
    }
    function next() {
        $this->position++;
    }
    function rewind() {
        $this->position = 0;
    }
    function valid() {
        return isset($this->keys[$this->position]);
    }

    // just fetches the first found entry
    function offsetGet($key) {
        if (($i = array_search($key, $this->keys)) !== FALSE) {
            return $this->values[$i];
        }
        else trigger_error("Undefined offset '$key'", E_USER_NOTICE);
    }

    // will only append new entries, not overwrite existing
    function offsetSet($key, $value) {
        $this->keys[] = $key;
        $this->values[] = $value;
    }

    // removes first matching entry
    function offsetUnset($key) {
        if (($i = array_search($key, $this->keys)) !== FALSE) {
            unset($this->keys[$i]);
            unset($this->values[$i]);
            // keep entries continuos for iterator
            $this->keys = array_values($this->keys);
            $this->values = array_values($this->values);
        }
    }
    function offsetExists($key) {
        return array_search($key, $this->keys) !== FALSE;
    }
}
2
mario

Comme le dit porneL, l’intérêt des tableaux est que les clés sont uniques.

Si vous souhaitez référencer plusieurs entrées dans un tableau, vous devez rechercher les valeurs du tableau. 

  $arr=array(
     0=>array('date'=>time(), 'ip'=>'127.0.0.1', url='index.php'),
     1=>array('date'=>time(), 'ip'=>'192.168.1.2', url='index.php'),
     2=>array('date'=>time(), 'ip'=>'127.0.0.1', url='other.php'));
  $matches=retrieve_keys_matching_subkey($arr, 'ip', '127.0.0.1');
  foreach ($matches as $i) {
     print implode(' ', $arr[$i]) . "\n";
  }

  function retrieve_keys_matching_subkey($arr, $subkey, $value)
  {
     $out=array();
     foreach ($arr as $key=>$sub) {
         if ($sub[$subkey]===$value) {
             $out=$key;
         }
     }
     return $out;
  }

Cela sera évidemment plus efficace si vous maintenez des index. Le code pour cela n'est pas trivial.

Si vous travaillez avec de grands ensembles de données, il est fortement recommandé d'utiliser un SGBD pour gérer les données. Si ce n'est pas pratique, utilisez une liste chaînée.

1
symcbean

Ne peut être atteint que par un tableau multidimensionnel

0
user431949