web-dev-qa-db-ja.com

PHP配列で重複キーを許可する方法

PHP配列に重複キーを許可するにはどうすればよいですか?キーと値のペアを既存のキーと挿入しようとすると、対応する以前のキーの値が新しい値で上書きされます。異なる値を持つ両方の重複キーを維持できる方法はありますか?

38
sudh

配列(別名多次元配列)の値を持つ単一のキーを持つことができます。これには、その特定のキーを持つすべての要素が含まれます。例は

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

次に、次の方法でアクセスします。

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

等。

このメソッドを使用して配列の配列を作成していることに注意してください。

30
Matthew

配列のポイントは、一意のキーを持つことです。値のペアを保存する場合:

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

多数のデュープがある場合、この代替はより効率的です:

<?php

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

PHPはこれを許可していません。最善の解決策は、多次元配列を使用することです。例えば...

<?php

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

?>

key1という名前の重複キーがあることに注意してください。

key1の各インスタンスを呼び出したい場合は、実行します

<?php

    $desiredKeyName = "key1";

    foreach ($mArray as $aValue) {

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

            if ($key == $desiredKeyName) {

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

?>

そしてそれは戻ります

value1
value4
7
Steve Robbins

紹介します:アレイのアーカイブ

サンプル使用法。

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

クラスコード

<?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:仕事を早く終わらせるには、このようなハックが必要な場合があります!

彼の質問には強い論争があり、コンピューターサイエンスの教えに反しています。 (撮影する前に、すべてを読んでください)しかし、これを実現したい場合があります。 = X

たとえば、指定された配列オブジェクトのセットを操作するコードベースがあります。そして、繰り返し使用されるため(ループ?、再帰?)。結果をオーバーライドまたは再定義します。最終セットが提供されるまで。

そして、すべてが完了したとき。突然、クライアント(または自分の)仕様が変更されたことがわかります。最終的なデータの代わりに、すべてのデータを中間に配置する必要があります(したがって、キーごとに1つ以上のデータが必要です)。そして、残念なことに、システムはすでにこのような複雑な方法で完成しているので、!ダイナミックコール)。それで何をしますか?

これは最近私が遭遇したシナリオでしたが、このための簡単なハックがあり、古いデータを保持しながら、すべてのコードが引き続き機能することを保証します。

最終結果は、他のオブジェクトと同様に扱うことができるクラスです。ただし、古いデータを保持するためのアーカイブ機能があります。 [0]インデックスに直接アクセスして、多次元配列をソートします。そして、このオブジェクトで変数宣言を変更するだけで機能します。また、オブジェクトパラメータに加えられた変更はアーカイブされます。簡単にアクセスできるように、コードプログラム全体を最小限またはまったく変更せずに=)

5
PicoCreator

私は個人的なプロジェクトに取り組んでいる間に簡単な解決策を思いつきました。

私はある種の複製されたキーが欲しかったので、配列key => valuesを逆順value => keyに保存することにしました。ここで、valueはキーになり、keyは値になります。値。この特定のケースで機能するように、重複する値を作成していません。

ちょっとした例:

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

次のようなものがある場合:

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

次に、別のソリューションを使用します。

4
darth lemon

「できません」というほどではありません。重複キーを持つ配列の欠点は、実際に使用しようとしたときに明らかになります。

  • コンテンツを個別にアドレス指定する機能を失います。ために $array['duplicate']アクセスすると、最初のエントリのみが表示されます。
  • そのため、実際には、あいまいさに関係なく各キー/値のペアを見るforeachでそのようなオブジェクトのみを使用できます。
  • 以下を参照して、設定解除の試行を処理する方法、またはエントリをまったく上書きできるかどうかも決定する必要があります。追加専用モードを実装するのが最も簡単です。 (そして、これは理にかなっているかもしれないegdeケースです。)

とにかく、質問への逐語的な回答を得るには:PHPs配列構文を使用できますが、代わりに次のような累積オブジェクトを使用できます。

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

PorneLが言うように、配列の重要なポイントは、キーが一意であることです。

配列内の複数のエントリを参照する場合は、配列値を検索する必要があります。

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

インデックスを維持する場合、これは明らかにより効率的になります。このためのコードは簡単ではありません。

大規模なデータセットを使用している場合は、DBMSを使用してデータを管理することを強くお勧めします。それが実用的でない場合は、リンクリストを使用してください。

1
symcbean

多次元配列を介してのみ達成可能

0
user431949