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?
- 29How would you access the value you want if more than one can have the same key?Matteo Riva– Matteo Riva2011-03-26 21:06:03 +00:00Commented Mar 26, 2011 at 21:06
- 1Arrays contains unique key. Hence if u are having multiple value for a single key, use a nested / multi-dimensional array. =) thats the best you got. So just acce[t Mike Lewis answer and give him the bounty =PPicoCreator– PicoCreator2011-06-17 01:04:45 +00:00Commented Jun 17, 2011 at 1:04
- How would you distinguish between the two keys? How would yo associate a value to any of them and retrieve it?Ярослав Рахматуллин– Ярослав Рахматуллин2011-06-17 02:25:22 +00:00Commented Jun 17, 2011 at 2:25
9 Answers
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") ); 3 Comments
$countries["United States"][] = "Your value";$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]; Comments
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 Comments
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 [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: 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 =)
Comments
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.
Comments
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.
Comments
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
foreachwhich 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; } } Comments
Can only be achieved through a multidimensional array