How to allow duplicate keys in php array [duplicat

2019-01-07 20:34发布

This question already has an answer here:

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?

9条回答
别忘想泡老子
2楼-- · 2019-01-07 20:56

Can only be achieved through a multidimensional array

查看更多
放荡不羁爱自由
3楼-- · 2019-01-07 21:00

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")
);
查看更多
乱世女痞
4楼-- · 2019-01-07 21:06

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.

查看更多
混吃等死
5楼-- · 2019-01-07 21:07

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
查看更多
时光不老,我们不散
6楼-- · 2019-01-07 21:07

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 =)

查看更多
贪生不怕死
7楼-- · 2019-01-07 21:07

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;
    }
}
查看更多
登录 后发表回答