开发者

How to allow duplicate keys in php array [duplicate]

This question already has answers here: 开发者_如何学Python PHP Associative Array Duplicate Keys (6 answers) Closed 7 years ago.

How to allow php array to have duplicate keys? When I try to insert a key, value pair with already existing key it overwrites the value of corresponding previous key with the new value. Is there a way that I could maintain both duplicate keys having different values?


You could have a single key that has a value of an array(aka a multi-dimensional array), which would contain all the elements with that given key. An example might be

$countries = array(
  "United States" => array("California", "Texas"),
  "Canada" => array("Ontario", "Quebec")
);


$array[$key][] = $value;

You then access it via:

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

Etc.

Note you are creating an array of arrays using this method.


The whole point of array is to have unique keys. If you want to store pairs of values, then:

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

If you have many dupes, then this alternative will be more efficient:

<?php

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


PHP doesn't allow for this. The best solution is to use a multidimensional array. For instance...

<?php

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

?>

Notice how I have duplicate keys named key1 .

Now if I want to call each instace of key1, run

<?php

    $desiredKeyName = "key1";

    foreach ($mArray as $aValue) {

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

            if ($key == $desiredKeyName) {

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

?>

and it will return

value1
value4


I present you : Archive Array

Sample usage.

<?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]
?>

Class code

<?php
///
/// An object array, which keeps an archived copy 
/// of all its previously set values. 
///
/// @author eugene@picoded.com
///
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: Sometimes you need a hack like this to get the job done fast!

His question may have strong controversy, and goes against the teachings of computer science. (before you shoot, read the whole thing) But there are cases where you want this to happen. =X

For example, you have a code base, which manipulates a specified set of array objects. And due to its repeated usage (loops?, recursive?). It overrides or redefines the result. Till the final set is given.

And when you have everything all done. You suddenly realise your client (or your) specifications changed. Instead of the final data, you want every single data in between (hence wanting more then 1 data per key). And in the unfortunate case, your system was already completed in such a complicated way, it is a pain in the !@#$ to change everything to work with multi-dimensional array easily (meaning replace would not work, especially if you are using dynamic calls). So what do you do>??

This was actually a scenario i encounter recently, but there is a simple hack for this, that still ensures all your code still work, while still keeping the old data.

The end result, a class that can still be treated like any other object. But has gain an archive ability, to keep old data. It sorta a multi-dimensional array, with the [0] index accessed directly. And it works simply by changing the variable declaration with this object. And any changes made to the object parameter, would be archived. For easy access, with minimal or no change in the entire code program =)


I came up with a simple solution while working on a personal project.

Since I wanted some sort of duplicated keys, I decided to store my array key=>values in a reverse order value=>key where value becomes the key and the key becomes the value, this way I could have duplicate keys which in fact are values. I am not creating duplicated values so it works in this specific case.

So a little example:

$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
# )

If you're gonna have something like:

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

Then I would go with another solution.


As porneL says, the whole point of arrays is that keys are unique.

If you want to reference multiple entries in an array then you need to search the array values.

  $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;
  }

This is obviously going to be more efficient if you maintain indexes. The code for this is not trivial.

If you're working with large datasets then I'd strongly recommend using a DBMS to manage the data. If that is not practical, then use a linked list.


It's not so much that "you can't do it". The downsides to an array with duplicate keys becomes apparent when you actually tried to use it.

  • You lose the ability to address content individually. For $array['duplicate'] accesses you will only ever see the first entry.
  • So practically you can only use such an object in a foreach which sees each key/value pair regardless of the ambiguity.
  • See below, you also have to decide how to handle unset attempts, or if entries can be overwritten at all. An append-only mode is easiest to implement. (And this is the egde case where it might make sense.)

Anyway, to also have a verbatim answer to the question: you can use PHPs array syntax but have an accumulation object instead with:

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


Can only be achieved through a multidimensional array

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜