PHP配列が連想的または順次的であるかどうかを確認する方法は?


781

PHPはすべての配列を連想配列として扱うため、組み込み関数はありません。配列に数値キーのみが含まれているかどうかを確認するかなり効率的な方法を誰かが推奨できますか?

基本的に、私はこれを区別できるようにしたいです:

$sequentialArray = array('apple', 'orange', 'tomato', 'carrot');

この:

$assocArray = array('fruit1' => 'apple', 
                    'fruit2' => 'orange', 
                    'veg1' => 'tomato', 
                    'veg2' => 'carrot');

382
コードにバグがあります。トマトは果物です。
OlleHärstedt2016年

9
この方法には注意点がありますが、多くの場合、私はそうするだけですif (isset($array[0]))。これは単純で高速です。もちろん、最初に配列が空でないことを確認し、メソッドが失敗しないように、配列の可能な内容についてある程度の知識が必要です(数値と連想の混合、または非順次など)。
Gras Double

@OlleHärstedt 米国高等裁判所によれば。;-)
MC皇帝

回答:


622

まったく同じではない2つの質問をしました。

  • まず、配列に数値キーのみがあるかどうかを判断する方法
  • 次に、配列に0から始まる連続した数値キーがあるかどうかを判断する方法

実際に必要なこれらの動作を検討してください。(どちらかがあなたの目的のために行うことになるかもしれません。)

最初の質問(単にすべてのキーが数値であることを確認する)は、キャプテンkurOによってよく答えられます

2番目の質問(配列のインデックスがゼロでシーケンシャルかどうかの確認)については、次の関数を使用できます。

function isAssoc(array $arr)
{
    if (array() === $arr) return false;
    return array_keys($arr) !== range(0, count($arr) - 1);
}

var_dump(isAssoc(['a', 'b', 'c'])); // false
var_dump(isAssoc(["0" => 'a', "1" => 'b', "2" => 'c'])); // false
var_dump(isAssoc(["1" => 'a', "0" => 'b', "2" => 'c'])); // true
var_dump(isAssoc(["a" => 'a', "b" => 'b', "c" => 'c'])); // true

32
非常にエレガントなソリューション。空の配列の(あいまいな)ケースではTRUEを返すことに注意してください。
Jonathan Lidbeck 16年

30
連想配列の特殊なケースとしてシーケンシャル配列を考えるほうが便利だと思います。したがって、すべての配列は連想的ですが、連続しているのは一部だけです。したがって、関数isSequential()はよりも意味がありisAssoc()ます。このような関数では、空の配列シーケンシャルと見なされます。式は次のようになりますarray() === $arr || !isAssoc($arr)
ドンキホーテ2016

18
これは、すべてのキーを抽出する前にisset($ arr [0])がfalseかどうかをチェックして、配列が空ではないが0に要素がない場合は明らかに関連性があるので、潜在的なCPU時間とメモリの多くを回避すると思いますポジション。「ほとんどの」実際の連想配列はキーとして文字列を持っているので、これはそのような関数の一般的な場合に最適な最適化になるはずです。
OderWat

10
@OderWat- ゼロ要素がnull値の場合、issetは誤ってfalseを返すため、array_key_exists代わりに最適化を使用する必要issetがあります。null値は通常、そのような配列では正当な値である必要があります。
OCDev

@MAChitgarha、あなたの編集は、理由の説明なしで関数の動作を変更し、それが実際に何をすべきかについての上記の散文の説明と矛盾しました。元に戻しました。
マークアメリー2018

431

配列に整数以外のキーがあるかどうかを単に確認するには(配列に順次インデックスが付けられているか、ゼロインデックスが付けられているかではありません):

function has_string_keys(array $array) {
  return count(array_filter(array_keys($array), 'is_string')) > 0;
}

文字列キーが少なくとも1つある場合は$array、連想配列と見なされます。


22
この方法は、見かけよりもはるかに優れています。count(filtered_array)== count(original_array)の場合、それは連想配列です。count(filtered_array)== 0の場合、それはインデックス付き配列です。count(filtered_array)<count(original_array)の場合、配列には数値キーと文字列キーの両方があります。
Jamol 2012年

5
@MikePretzlaw もちろん反復します。(明らかに)配列のすべてのキーを調べずに、配列のすべてのキーがintであるかどうかを判断する方法はありません。以下に表示されるはずの非反復の代替案は、次のようなものだと思い$isIndexed = array_values($arr) === $arr;ますか?私が尋ねるところ:どのように機能すると思いarray_values()ますか?===配列に適用するとどのように機能すると思いますか?答えはもちろん、配列に対して繰り返し処理することです。
Mark Amery

4
@ARW 「可能な場合、PHPはすべてを配列定義のintにキャストするようです。」-うん、それはまさに何が起こるかです。最大のWTFは、これをフロートすることさえするということです。試してみるとvar_dump([1.2 => 'foo', 1.5 => 'bar']);、配列を取得していることがわかり[1 => 'bar']ます。キーの元のタイプを見つける方法はまったくありません。はい、これはすべてひどいです。PHPの配列は言語の中で最悪の部分であり、損傷のほとんどは元に戻せません。これは、従来の配列に単一の構成体を使用し、従来のハッシュマップを最初からひどいものにしたためです。
Mark Amery

30
@MarkAmery上記は、単純ですが、配列の100%ウォークを保証します。特に大規模な配列を処理している場合、stringまたはintをチェックしていて、最初に見つかった部分で問題が発生した場合は、より効率的です。例えば: function isAssociative($arr) { foreach ($arr as $key => $value) { if (is_string($key)) return true; } return false; }
思考の

1
@Thoughtコードは非常に高速に動作しますが、シーケンシャル配列を検出できません。(連想配列)になるはずの例array(1 => 'a', 0 => 'b', 2 => 'c')false(順次配列)になりますtruetoolsqa.com/data-structures/array-in-programming キーが昇順である必要があるかどうかわかりませんか?(0、1、...)
VEE

132

確かにこれはより良い代替手段です。

<?php
$arr = array(1,2,3,4);
$isIndexed = array_values($arr) === $arr;

52
これは配列の値を複製します。これは潜在的に非常に高価です。配列のキーを調べた方がはるかによいでしょう。
貧乳

8
私は==を使用しました。ここでは===は必要ないと思います。しかし、「設定を解除して機能しない」と答えると、最初の要素の設定を解除すると、0から始まる整数インデックス付き配列ではなくなります。そのため、IMOは機能します。
grantwparks 2012

4
@grantwparksに同意する:スパース配列はインデックス付けされません。興味深いことに、インデックス付き配列の途中から実際に要素を削除する方法はないため、PHPは基本的にすべての配列を連想配列として宣言し、数値は単なる「私にとって重要なキー」バージョンです。
RickMeasham 2013年

7
これに関して私が抱えている唯一の問題===は、キーのみに関心がある場合でも、値が等しいかどうかのチェックに時間がかかるということです。このため、私は$k = array_keys( $arr ); return $k === array_keys( $k );バージョンを好みます。
Jesse

5
追加の注記、これは、順序が乱れている数値キーで指定された配列では失敗します。例:$ myArr = array(0 => 'a'、3 => 'b'、4 => 1、2 => 2、1 => '3'); 回避策の1つは、テストを実行する前にksort($ arr)を実行することです
Scott

77

この質問の多くのコメント者は、配列がPHPでどのように機能するかを理解していません。配列のドキュメントから:

キーは整数または文字列のいずれかです。キーが整数の標準表現である場合、そのように解釈されます(つまり、「8」は8と解釈され、「08」は「08」と解釈されます)。キーの浮動小数点数は整数に切り捨てられます。インデックス付きの連想配列型はPHPと同じ型で、整数と文字列の両方のインデックスを含めることができます。

つまり、配列キー「8」は常に(サイレントに)整数8に変換されるため、配列キーなどはありません。したがって、整数と数値文字列を区別する必要はありません。

配列の一部(array_keys()のように)またはそのすべて(foreachのように)のコピーを作成せずに、配列の整数でないキーをチェックする最も効率的な方法が必要な場合:

function keyedNext( &$arr, &$k){
    $k = key($arr);
    return next($arr);
}

for ($k = key(reset($my_array)); is_int($k); keyedNext($my_array,$k))
    $onlyIntKeys = is_null($k);

これが機能するのは、現在の配列位置が無効であり、NULLが有効なキーではない場合、key()がNULLを返すためです(配列キーとしてNULLを使用しようとすると、暗黙的に ""に変換されます)。


これは、連続しない整数キーでは機能しません。[2 => 'a'、4 => 'b']で試してください。
DavidJ

2
@DavidJ、「動作しない」とはどういう意味ですか?すべてのキーが整数であると正常に判別されます。あなたが投稿したような配列は「数値配列」と見なされるべきではないと主張していますか?
coredumperror

7
非連想配列の範囲キー有していなければならない0のがcount($array)-1、この厳密な順序で、。の予備チェックis_array()が役立つ場合があります。増加する変数を追加して、キーシーケンスを確認します。for ($k = 0, reset($array) ; $k === key($array) ; next($array)) ++$k;これで取引が解決します。
ofavre 2013年

2
foreach明示的な反復の代わりに使用すると、約2倍速くなります。
ofavre 2013年

1
これを関数にしたい場合: function isAssocStr($array) { for (reset($array); is_int(key($array)); next($array)) { if (is_null(key($array))) return false; } return true; }
GreeKatrina

39

以下のようOPで述べました

PHPはすべての配列を連想配列として扱います

配列が連想であるかどうかをチェックする関数を書くことは、あまり賢明ではありません(IMHO)。まず最初に、PHP配列のキーは何ですか?:

キーは、のいずれかになります、整数または文字列

つまり、次の3つのケースが考えられます。

  • ケース1.すべてのキーが数値 / 整数です。
  • ケース2.すべてのキーが文字列です。
  • ケース3.一部のキーは文字列であり、一部のキーは数値 / 整数です。

それぞれのケースを以下の機能で確認できます。

ケース1:すべてのキーが数値 / 整数です。

この関数は、空の配列に対してもtrueを返します

//! Check whether the input is an array whose keys are all integers.
/*!
    \param[in] $InputArray          (array) Input array.
    \return                         (bool) \b true iff the input is an array whose keys are all integers.
*/
function IsArrayAllKeyInt($InputArray)
{
    if(!is_array($InputArray))
    {
        return false;
    }

    if(count($InputArray) <= 0)
    {
        return true;
    }

    return array_unique(array_map("is_int", array_keys($InputArray))) === array(true);
}

ケース2:すべてのキーが文字列です。

この関数は、空の配列に対してもtrueを返します

//! Check whether the input is an array whose keys are all strings.
/*!
    \param[in] $InputArray          (array) Input array.
    \return                         (bool) \b true iff the input is an array whose keys are all strings.
*/
function IsArrayAllKeyString($InputArray)
{
    if(!is_array($InputArray))
    {
        return false;
    }

    if(count($InputArray) <= 0)
    {
        return true;
    }

    return array_unique(array_map("is_string", array_keys($InputArray))) === array(true);
}

ケース3.一部のキーは文字列であり、一部のキーは数値 / 整数です。

この関数は、空の配列に対してもtrueを返します

//! Check whether the input is an array with at least one key being an integer and at least one key being a string.
/*!
    \param[in] $InputArray          (array) Input array.
    \return                         (bool) \b true iff the input is an array with at least one key being an integer and at least one key being a string.
*/
function IsArraySomeKeyIntAndSomeKeyString($InputArray)
{
    if(!is_array($InputArray))
    {
        return false;
    }

    if(count($InputArray) <= 0)
    {
        return true;
    }

    return count(array_unique(array_map("is_string", array_keys($InputArray)))) >= 2;
}

それはそれに従います:


さて、配列が私たち全員が慣れている「本物の」配列になるためには、

  • そのキーはすべて数値 / 整数です。
  • そのキーは順次です(つまり、ステップ1で増加します)。
  • キーはゼロから始まります

以下の機能で確認できます。

ケース3a。キーは、数値 / 整数シーケンシャルゼロベースです。

この関数は、空の配列に対してもtrueを返します

//! Check whether the input is an array whose keys are numeric, sequential, and zero-based.
/*!
    \param[in] $InputArray          (array) Input array.
    \return                         (bool) \b true iff the input is an array whose keys are numeric, sequential, and zero-based.
*/
function IsArrayKeyNumericSequentialZeroBased($InputArray)
{
    if(!is_array($InputArray))
    {
        return false;
    }

    if(count($InputArray) <= 0)
    {
        return true;
    }

    return array_keys($InputArray) === range(0, count($InputArray) - 1);
}

警告/落とし穴(または、PHPの配列キーに関するさらに特異な事実)

整数キー

これらの配列のキーは整数です:

array(0 => "b");
array(13 => "b");
array(-13 => "b");          // Negative integers are also integers.
array(0x1A => "b");         // Hexadecimal notation.

文字列キー

これらの配列のキーは文字列です:

array("fish and chips" => "b");
array("" => "b");                                   // An empty string is also a string.
array("stackoverflow_email@example.com" => "b");    // Strings may contain non-alphanumeric characters.
array("stack\t\"over\"\r\nflow's cool" => "b");     // Strings may contain special characters.
array('$tα€k↔øv∈rflöw⛄' => "b");                    // Strings may contain all kinds of symbols.
array("functіon" => "b");                           // You think this looks fine? Think again! (see https://stackoverflow.com/q/9246051/1402846)
array("ま말轉转ДŁ" => "b");                         // How about Japanese/Korean/Chinese/Russian/Polish?
array("fi\x0sh" => "b");                            // Strings may contain null characters.
array(file_get_contents("https://www.google.com/images/nav_logo114.png") => "b");   // Strings may even be binary!

文字列のように見える整数キー

キーarray("13" => "b")文字列であると考えるなら、あなたは間違っています。ここのドキュメントから

有効な整数を含む文字列は、整数型にキャストされます。たとえば、キー「8」は実際には8の下に格納されます。一方、「08」は有効な10進整数ではないため、キャストされません。

たとえば、これらの配列のキーは整数です:

array("13" => "b");
array("-13" => "b");                        // Negative, ok.

しかし、これらの配列の鍵は文字列です:

array("13." => "b");
array("+13" => "b");                        // Positive, not ok.
array("-013" => "b");
array("0x1A" => "b");                       // Not converted to integers even though it's a valid hexadecimal number.
array("013" => "b");                        // Not converted to integers even though it's a valid octal number.
array("18446744073709551616" => "b");       // Not converted to integers as it can't fit into a 64-bit integer.

さらに、ドキュメントによると、

整数のサイズはプラットフォームに依存しますが、最大値である約20億が通常の値(32ビットの符号付き)です。64ビットのプラットフォームの最大値は通常、約32ビットであるWindowsを除いて、約9E18です。PHPは符号なし整数をサポートしていません。

したがって、この配列のキーは整数である場合とそうでない場合があります-これはプラットフォームによって異なります。

array("60000000000" => "b");                // Array key could be integer or string, it can fit into a 64-bit (but not 32-bit) integer.

さらに悪いことに、整数が2 31 = 2,147,483,648の境界に近い場合、PHPはバグ多い傾向があります(バグ51430バグ52899を参照)。例えば、私のローカル環境(Windows 7でXAMPP 1.7.7のPHP 5.3.8)で、与えvar_dump(array("2147483647" => "b"))

array(1) {
    [2147483647]=>
    string(1) "b"
}   

しかし、上codepadでこのライブデモ(PHP 5.2.5)、同じ式が与えられます

array(1) {
    ["2147483647"]=>
    string(1) "b"
}

したがって、ある環境ではキーは整数ですが、別の環境では文字列です2147483647が、有効な符号付き32ビット整数です。


2
ただし、以下で説明するように、チェック対象の配列と重複する配列を作成する必要があるため、大規模な配列では非常に高価になり、共有ホストでメモリ不足によるクラッシュが発生する可能性があります。
ポッドパーソン2015年

35

スピードに関して:

function isAssoc($array)
{
    return ($array !== array_values($array));
}

メモリに関して:

function isAssoc($array)
{
    $array = array_keys($array); return ($array !== array_keys($array));
}

次の配列:array(02 => 11,1,2,456); 02 === 2の場合でも、上記のアルゴリズムを使用して数値キーがないと表示されます
Galileo_Galilei

20
function checkAssoc($array){
    return  ctype_digit( implode('', array_keys($array) ) );
}

2
これは(私のコメントの時点で)以下を処理できる唯一の回答です:$ array = array(0 => 'blah'、2 => 'yep'、3 => 'wahey')
Shabbyrobe

しかし、array('1'=>'asdf', '2'=>'too')実際にはそうではない(キーは実際には文字列である)間は、連想配列と見なされます
キャプテンkurO

1
@CaptainkurOあなたは数値を意味します。連想配列です。
devios1

1
この関数はtrue、キーがゼロ、整数(正のみ)、空の文字列、または上記の任意の組み合わせ(文字列 "09"など)の場合に返されます。この関数では、キーの順序は考慮されません。したがってarray(0=>'blah', 2=>'yep', 3=>'wahey')、この関数によるarray(0=>'blah', 2=>'yep', 1=>'wahey')array('blah', 'yep', 'wahey')、すべてが連想的ですが、そうでarray('a'=>'blah', 'b'=>'yep', 'c'=>'wahey')はありません。
2012年

@CaptainkurOあなたは間違っています。「1」と「2」は整数として格納されます。2011年5月11日19:34にリスの回答の引用部分を読んでください。PHPは、整数のように見える文字列キーを格納しません。それらを整数に変換します。
Buttle Butkus 2013年

20

実際、最も効率的な方法は次のとおりです。

function is_assoc($array){
   $keys = array_keys($array);
   return $keys !== array_keys($keys);
}

それは(になるキーのキーに(シーケンシャル配列のために、常に0,1,2などです)キーを比較ので、これは動作します常に 0,1,2なども)。


1
賢いですが、良くはありません。なぜこれが「最も効率的」なのですか?array_keys($ a)をrange(0、count($ a))と比較する方がずっと読みやすいでしょう。最も賢い解決策は、私の経験ではめったに最高のものではありません。特に賢いときは、明白でクリーンな代替手段よりも文字通り価値がありません。
シェーンH

4
この関数は、返すtrueためarray(1=>"a")けどfalseためにarray("a"=>"a")!=がに置き換えられた場合、より意味のあるものになります!==
2012年

1
@Pangあなたは正しいです。あなたのコメントは最初は確かに間違っているはずだと思いましたが、驚いたことに、[0] == ['a']PHPにあります(0 == 'a'、そして、確かに0 == 'banana')。PHPの==演算子は非常識です。
Mark Amery 2016年

2
array_keysを呼び出すのではなく、非順次整数インデックスが見つかるまでチェックするだけなので、効率的ではありません。とにかくそれをやっていますが、あなたはすでに大きな配列を複製しています。
ポッドパーソン2017年

17

私はarray_keys($obj) !== range(0, count($obj) - 1)andの両方を使用しましたarray_values($arr) !== $arr(2つはお互いのデュアルですが、2つ目は最初のものより安価です)が、非常に大きなアレイではどちらも失敗します。

これは、array_keysとのarray_values両方が非常にコストのかかる操作だからです(元の配列とほぼ同じサイズのまったく新しい配列が作成されるため)。

次の関数は、上記の方法よりも堅牢です。

function array_type( $obj ){
    $last_key = -1;
    $type = 'index';
    foreach( $obj as $key => $val ){
        if( !is_int( $key ) || $key < 0 ){
            return 'assoc';
        }
        if( $key !== $last_key + 1 ){
            $type = 'sparse';
        }
        $last_key = $key;
    }
    return $type;
}

まばらな配列と連想配列を区別する必要がない場合は、単純に 'assoc'、両方のifブロックからます。

最後に、これはこのページの多くの「ソリューション」よりも「エレガント」に見えないかもしれませんが、実際にははるかに効率的です。ほとんどすべての連想配列が即座に検出されます。インデックス付き配列のみが徹底的にチェックされ、上で概説した方法はインデックス付き配列を徹底的にチェックするだけでなく、それらを複製します。


13

次の2つの関数は、「配列が連想であるか数値であるか」をチェックするための最良の方法だと思います。「数値」は、数字キーのみ、または順次数字キーのみを意味する可能性があるため、以下のいずれかの条件をチェックする2つの関数をリストします。

function is_indexed_array(&$arr) {
  for (reset($arr); is_int(key($arr)); next($arr));
  return is_null(key($arr));
}

function is_sequential_array(&$arr, $base = 0) {
  for (reset($arr), $base = (int) $base; key($arr) === $base++; next($arr));
  return is_null(key($arr));
}

最初の関数は、各キーが整数値かどうかをチェックします。2番目の関数は、各キーが整数値であるかどうかをチェックし、さらにすべてのキーが$ baseから始まる順次であるかどうかをチェックします。デフォルトは0なので、別のベース値を指定する必要がない場合は省略できます。key($ my_array)は、読み取りポインターが配列の終わりを超えて移動するとnullを返します。これにより、forループが終了し、すべてのキーが整数の場合、forループの後のステートメントがtrueを返します。そうでない場合、キーが文字列型であるためループは時期尚早に終了し、forループの後のステートメントはfalseを返します。後者の関数はさらに、各比較の後に$ baseに1を追加して、次のキーが正しい値であるかどうかを確認できるようにします。厳密な比較により、キーが整数型かどうかもチェックされます。forbaseループの最初のセクションの$ base =(int)$ base部分は、$ baseが省略されている場合、または整数のみを使用して呼び出されることを確認している場合は省略できます。しかし、誰にとっても確信が持てないので、私はそれを残しました。ステートメントはとにかく一度だけ実行されます。私はこれらが最も効率的なソリューションだと思います:

  • メモリに関して:データまたはキー範囲のコピーはありません。array_valuesまたはarray_keysを実行すると、短く見える(コードが少なくなる)かもしれませんが、その呼び出しを行った後、バックグラウンドで何が行われているのかを覚えておいてください。はい、他のいくつかのソリューションよりも(目に見える)ステートメントが多くありますが、それは重要ではありませんか?
  • 時間に関して:データやキーのコピー/抽出にも時間がかかるという事実に加えて、このソリューションはforeachよりも効率的です。繰り返しになりますが、foreachは表記が短いため、一部の人にとってはより効率的に見えるかもしれませんが、バックグラウンドでforeachはreset、key、nextを呼び出してループします。しかし、それに加えて、終了条件をチェックするために有効を呼び出しますが、整数チェックとの組み合わせにより、ここでは回避されています。

配列キーは整数または文字列のみであり、「1」ではなく「1」などの厳密に数値の文字列は整数に変換されることに注意してください。これにより、配列をシーケンシャルにしたい場合にカウントする以外に、整数キーのチェックが唯一必要な操作になります。当然、is_indexed_arrayがfalseを返す場合、配列は連想的と見なすことができます。私は「見た」と言います。


1
これが最良の答えです。「連想」または「数値」配列の定義は、特定の状況によって異なります。
パト2013年

foreachがここで使用する方法よりも効率が悪い場合、2つの異なる関数を使用することの不便さを除けば、このソリューションのパフォーマンスは私のものよりも優れています(前述)。配列を処理する最速の方法としてforeachが推奨されるため、そうではないと思います。
podperson

7

この関数は次を処理できます。

  • インデックスに穴がある配列(例:1、2、4、5、8、10)
  • "0x"キーを持つ配列:たとえば、キー '08'は連想的ですが、キー '08'は連想的です。

考え方は単純です。キーの1つが整数でない場合、それは連想配列です。それ以外の場合、それは順次です。

function is_asso($a){
    foreach(array_keys($a) as $key) {if (!is_int($key)) return TRUE;}
    return FALSE;
}

1
「キーの1つが整数でない場合、それは連想配列であり、それ以外の場合は順次です」 -え?いいえ、これは単に間違っています。「連想」配列を構成するものについては議論の余地がありますが、「順次」の意味はかなり明確であり、すべてのキーが数値であるのと同じではありません。
Mark Amery

キーの1つが整数ではない場合、それは本質的に連想ですが、キーが0-長さ(配列)-1から始まる場合のみ連続します。ただし、すべてのキーに番号が付けられているだけの場合、NUMERICになりますが、または、順次配列を必要とする多くの配列関数では機能しない場合があります。array_values(array)を実行して穴のある数値配列をシーケンシャルに変換すると、シーケンシャルに変換されます。
geilt

7

使用して1:私は2つの人気のこの質問のためのアプローチを気づいたarray_values()し、他の使用しますkey()。どちらが速いかを調べるために、小さなプログラムを書きました。

$arrays = Array(
  'Array #1' => Array(1, 2, 3, 54, 23, 212, 123, 1, 1),
  'Array #2' => Array("Stack", 1.5, 20, Array(3.4)),
  'Array #3' => Array(1 => 4, 2 => 2),
  'Array #4' => Array(3.0, "2", 3000, "Stack", 5 => "4"),
  'Array #5' => Array("3" => 4, "2" => 2),
  'Array #6' => Array("0" => "One", 1.0 => "Two", 2 => "Three"),
  'Array #7' => Array(3 => "asdf", 4 => "asdf"),
  'Array #8' => Array("apple" => 1, "orange" => 2),
);

function is_indexed_array_1(Array &$arr) {
  return $arr === array_values($arr);
}

function is_indexed_array_2(Array &$arr) {
  for (reset($arr), $i = 0; key($arr) === $i++; next($arr))
    ;
  return is_null(key($arr));
}

// Method #1
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
  foreach ($arrays as $array) {
    $dummy = is_indexed_array_1($array);
  }
}
$end = microtime(true);
echo "Time taken with method #1 = ".round(($end-$start)*1000.0,3)."ms\n";

// Method #2
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
  foreach ($arrays as $array) {
    $dummy = is_indexed_array_2($array);
  }
}
$end = microtime(true);
echo "Time taken with method #1 = ".round(($end-$start)*1000.0,3)."ms\n";

CentOSのPHP 5.2でのプログラムの出力は次のとおりです。

メソッド#1でかかった
時間= 10.745ms メソッド#2 でかかった時間= 18.239ms

PHP 5.3での出力でも同様の結果が得られました。明らかに使用array_values()ははるかに高速です。


悪いベンチマーク。大きな配列はテストしませんでした。10K以上の要素で始まる私のコンピューターでは、方法#2が高速です。試してみる$arrays = Array( 'Array #1' => range(0, 50000), );
nonsensei

7

これに取り組む1つの方法は、に便乗json_encodeすることです。これには、正しいJSONを出力するために、連想配列とインデックス付き配列を区別する独自の内部メソッドがすでにあります。

これを行うには、エンコード後に返される最初の文字が{(連想配列)か[(インデックス付き配列)かを確認します。

// Too short :)
function is_assoc($arr) {
    ksort($arr);
    return json_encode($arr)[0] === '{';
}

ksort()は私の意見では必要ありません。このソリューションは機能していますが、$ arrがnullかどうか、json_encodeが失敗したかどうかをテストする必要があるため、try / catchを実行します。+ $ arrが大きい場合は、実際には最適ではありません。
lucbonnin

7

すでに多くの回答がありますが、LaravelがArrクラス内で依存しているメソッドは次のとおりです。

/**
 * Determines if an array is associative.
 *
 * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
 *
 * @param  array  $array
 * @return bool
 */
public static function isAssoc(array $array)
{
    $keys = array_keys($array);

    return array_keys($keys) !== $keys;
}

ソース:https : //github.com/laravel/framework/blob/5.4/src/Illuminate/Support/Arr.php


1
@Casey array_keys($keys)は、元の配列と同じ長さの数値の連続配列(0 ... X)を返します。例えばarray_keys(["a", "b", "c"]) = [0, 1, 2]; array_keys([0, 1, 2]) = [0, 1, 2](それはシーケンシャル配列だからです[0, 1, 2] !== [0, 1, 2])。別の例:(array_keys(["a" => 5, "b" => 7, "c" => 10]) = ["a", "b", "c"]; array_keys(["a", "b", "c"]) = [0, 1, 2]それが連想配列だからです["a", "b", "c"] !== [0, 1, 2])。それが明確であることを願っています(少なくとも私にとっては、コメントで広く説明するのは難しい)
valepu

このアルゴリズムはクレイジーで、簡単で、理解できます。
Benyi、

連想行の順次配列がある場合、これは機能しません。
lucbonnin 2018年

5
function array_is_assoc(array $a) {
    $i = 0;
    foreach ($a as $k => $v) {
        if ($k !== $i++) {
            return true;
        }
    }
    return false;
}

高速、簡潔、メモリ効率が高い。高価な比較、関数呼び出し、配列のコピーは不要です。


4

xarray PHP拡張を使用する

これは非常に高速に実行できます(PHP 5.6では約30倍以上高速)。

if (array_is_indexed($array)) {  }

または:

if (array_is_assoc($array)) {  }

3

この巨大なキューに回答を追加するのは少し無意味であることはわかっていますが、値を複製する必要がない、読み取り可能なO(n)ソリューションを次に示します。

function isNumericArray($array) {
    $count = count($array);
    for ($i = 0; $i < $count; $i++) {
        if (!isset($array[$i])) {
            return FALSE;
        }
    }
    return TRUE;
}

むしろあなたは反復は、キーの上に、彼らはすべての数値かどうかを確認するためにキーをチェックします数値配列のためにそこにと彼らが存在します。


もう一点。フォームの配列[1,2,null,4]は失敗しますが、正しい配列です。そのため、stackoverflow.comarray_key_exists
a / 25206156/501831に

-1; @lazycommitで指摘されているisset()ように、値が設定されている場合はfalseを返すため、ここでは間違ったツールですnull
Mark Amery

3

私の解決策:

function isAssociative(array $array)
{
    return array_keys(array_merge($array)) !== range(0, count($array) - 1);
}

array_merge単一の配列ですべてのintegerキーのインデックスを再作成しますが、その他のインデックスは再作成しません。例えば:

array_merge([1 => 'One', 3 => 'Three', 'two' => 'Two', 6 => 'Six']);

// This will returns [0 => 'One', 1 => 'Three', 'two' => 'Two', 2 => 'Six']

したがって、リスト(非連想配列)が作成されると、['a', 'b', 'c']値が削除されてunset($a[1])からarray_merge呼び出され、リストのインデックスは0から始まります。


-1; これはO(n)使用される追加のメモリ内にあります(それはと同じくらい多くの要素を持つ複数の新しい配列を作成したため$array)、答えは質問された質問のあいまいさを解決せず、リスト/非連想配列を定義する方法を正確に説明しません。これらの点のどちらも当てはまらない場合、すでに投稿されている他の回答と比較して、これが何らかの価値を追加するかどうかは不明です。
Mark Amery

3

いくつかのローカルベンチマーク、デバッグ、コンパイラープローブ、プロファイリング、および3v4l.orgを悪用して、より多くのバージョンでベンチマークを実行し(はい、停止するようにという警告が表示されました)、見つけたすべてのバリエーションと比較しました...

私はあなたに有機的に派生与える最高の平均-最悪の場合のシナリオである連想配列テスト機能最悪大体いいとして、あるいは他のすべての平均的なケースのシナリオよりも良いように。

/**
 * Tests if an array is an associative array.
 *
 * @param array $array An array to test.
 * @return boolean True if the array is associative, otherwise false.
 */
function is_assoc(array &$arr) {
    // don't try to check non-arrays or empty arrays
    if (FALSE === is_array($arr) || 0 === ($l = count($arr))) {
        return false;
    }

    // shortcut by guessing at the beginning
    reset($arr);
    if (key($arr) !== 0) {
        return true;
    }

    // shortcut by guessing at the end
    end($arr);
    if (key($arr) !== $l-1) {
        return true;
    }

    // rely on php to optimize test by reference or fast compare
    return array_values($arr) !== $arr;
}

から https://3v4l.org/rkieX

<?php

// array_values
function method_1(Array &$arr) {
    return $arr === array_values($arr);
}

// method_2 was DQ; did not actually work

// array_keys
function method_3(Array &$arr) {
    return array_keys($arr) === range(0, count($arr) - 1);
}

// foreach
function method_4(Array &$arr) {
    $idx = 0;
    foreach( $arr as $key => $val ){
        if( $key !== $idx )
            return FALSE;
        ++$idx;
    }
    return TRUE;
}

// guessing
function method_5(Array &$arr) {
    global $METHOD_5_KEY;
    $i = 0;
    $l = count($arr)-1;

    end($arr);
    if ( key($arr) !== $l )
        return FALSE;

    reset($arr);
    do {
        if ( $i !== key($arr) )
            return FALSE;
        ++$i;
        next($arr);
    } while ($i < $l);
    return TRUE;
}

// naieve
function method_6(Array &$arr) {
    $i = 0;
    $l = count($arr);
    do {
        if ( NULL === @$arr[$i] )
            return FALSE;
        ++$i;
    } while ($i < $l);
    return TRUE;
}

// deep reference reliance
function method_7(Array &$arr) {
    return array_keys(array_values($arr)) === array_keys($arr);
}


// organic (guessing + array_values)
function method_8(Array &$arr) {
    reset($arr);
    if ( key($arr) !== 0 )
        return FALSE;

    end($arr);
    if ( key($arr) !== count($arr)-1 )
        return FALSE;

    return array_values($arr) === $arr;
}

function benchmark(Array &$methods, Array &$target, $expected){    
    foreach($methods as $method){
        $start = microtime(true);
        for ($i = 0; $i < 2000; ++$i) {
            //$dummy = call_user_func($method, $target);
            if ( $method($target) !== $expected ) {
                echo "Method $method is disqualified for returning an incorrect result.\n";
                unset($methods[array_search($method,$methods,true)]);
                $i = 0;
                break;
            }
        }
        if ( $i != 0 ) {
            $end = microtime(true);
            echo "Time taken with $method = ".round(($end-$start)*1000.0,3)."ms\n";
        }
    }
}



$true_targets = [
    'Giant array' => range(0, 500),
    'Tiny array' => range(0, 20),
];


$g = range(0,10);
unset($g[0]);

$false_targets = [
    'Large array 1' => range(0, 100) + ['a'=>'a'] + range(101, 200),
    'Large array 2' => ['a'=>'a'] + range(0, 200),
    'Tiny array' => range(0, 10) + ['a'=>'a'] + range(11, 20),
    'Gotcha array' => $g,
];

$methods = [
    'method_1',
    'method_3',
    'method_4',
    'method_5',
    'method_6',
    'method_7',
    'method_8'
];


foreach($false_targets as $targetName => $target){
    echo "==== Benchmark using $targetName expecing FALSE ====\n";
    benchmark($methods, $target, false);
    echo "\n";
}
foreach($true_targets as $targetName => $target){
    echo "==== Benchmark using $targetName expecting TRUE ====\n";
    benchmark($methods, $target, true);
    echo "\n";
}

2

これが私が使う方法です:

function is_associative ( $a )
{
    return in_array(false, array_map('is_numeric', array_keys($a)));
}

assert( true === is_associative(array(1, 2, 3, 4)) );

assert( false === is_associative(array('foo' => 'bar', 'bar' => 'baz')) );

assert( false === is_associative(array(1, 2, 3, 'foo' => 'bar')) );

これは次のような特別な場合を考慮していないことに注意してください。

$a = array( 1, 2, 3, 4 );

unset($a[1]);

assert( true === is_associative($a) );

申し訳ありません、それをお手伝いすることはできません。また、不要なコピーを作成しないため、まともなサイズの配列に対して多少パフォーマンスが向上します。PythonとRubyを書くのがとても良くなるのはこれらの小さなことです...:P


2
<?php

function is_list($array) {
    return array_keys($array) === range(0, count($array) - 1);
}

function is_assoc($array) {
    return count(array_filter(array_keys($array), 'is_string')) == count($array);
}

?>

ほとんどのポイントを獲得したこれらの例は、次のような配列では正しく機能しません $array = array('foo' => 'bar', 1)


+1あなたのis_list()はIMOが最良の答えです。時間と空間の複雑さ、およびネイティブとPHPのスクリプト関数について手がかりがない人もいます...
e2-e4

2

これも機能します(デモ):

function array_has_numeric_keys_only(array $array)
{
    try {
        SplFixedArray::fromArray($array, true);
    } catch (InvalidArgumentException $e) {
        return false;
    }
    return true;
}

この回答の主なポイントは、存在することを通知することでSplFixedArrayあり、これらの種類のテストに例外を使用するよう勧めることではないことに注意してください。


2

スカラー配列の定義はアプリケーションによって異なると思います。つまり、一部のアプリケーションでは、スカラー配列として適格なものについてより厳密な感覚が必要であり、一部のアプリケーションでは、より緩い感覚が必要になります。

以下に、厳密さを変える3つの方法を示します。

<?php
/**
 * Since PHP stores all arrays as associative internally, there is no proper
 * definition of a scalar array.
 * 
 * As such, developers are likely to have varying definitions of scalar array,
 * based on their application needs.
 * 
 * In this file, I present 3 increasingly strict methods of determining if an
 * array is scalar.
 * 
 * @author David Farrell <DavidPFarrell@gmail.com>
 */

/**
 * isArrayWithOnlyIntKeys defines a scalar array as containing
 * only integer keys.
 * 
 * If you are explicitly setting integer keys on an array, you
 * may need this function to determine scalar-ness.
 * 
 * @param array $a
 * @return boolean
 */ 
function isArrayWithOnlyIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    foreach ($a as $k => $v)
        if (!is_int($k))
            return false;
    return true;
}

/**
 * isArrayWithOnlyAscendingIntKeys defines a scalar array as
 * containing only integer keys in ascending (but not necessarily
 * sequential) order.
 * 
 * If you are performing pushes, pops, and unsets on your array,
 * you may need this function to determine scalar-ness.
 * 
 * @param array $a
 * @return boolean
 */ 
function isArrayWithOnlyAscendingIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    $prev = null;
    foreach ($a as $k => $v)
    {
        if (!is_int($k) || (null !== $prev && $k <= $prev))
            return false;
        $prev = $k;
    }
    return true;
}

/**
 * isArrayWithOnlyZeroBasedSequentialIntKeys defines a scalar array
 * as containing only integer keys in sequential, ascending order,
 * starting from 0.
 * 
 * If you are only performing operations on your array that are
 * guaranteed to either maintain consistent key values, or that
 * re-base the keys for consistency, then you can use this function.
 * 
 * @param array $a
 * @return boolean
 */
function isArrayWithOnlyZeroBasedSequentialIntKeys(array $a)
{
    if (!is_array($a))
        return false;
    $i = 0;
    foreach ($a as $k => $v)
        if ($i++ !== $k)
            return false;
    return true;
}

2

ソースからもう1つ速い。json_encode(およびbson_encode)のエンコードを調整します。したがって、javascript配列に準拠しています。

function isSequential($value){
    if(is_array($value) || ($value instanceof \Countable && $value instanceof \ArrayAccess)){
        for ($i = count($value) - 1; $i >= 0; $i--) {
            if (!isset($value[$i]) && !array_key_exists($i, $value)) {
                return false;
            }
        }
        return true;
    } else {
        throw new \InvalidArgumentException(
            sprintf('Data type "%s" is not supported by method %s', gettype($value), __METHOD__)
        );
    }
}

1
なぜissetそしてarray_key_exists?後者で十分ではないでしょうか?
mcfedr 2015年

@mcfedrはい、そうです- isset()ここでのチェックは完全に冗長です。
Mark Amery

@ mcfedr、@ mark-ameryは、パフォーマンス上の理由によるものです。isset()よりも速いですarray_key_exists()ilia.ws/archives/…を
lazycommit

@lazycommitその配列に依存するかどうかは、配列の有無にかかわらず、多数のnulls を含む配列がある可能性が高いわけではありませんが、顕著なパフォーマンスの違いがあるほど十分に大きい配列である可能性も低いです。両方のチェックを使用する
mcfedr

2
あなたはそれが合うでしょうかどうかを確認する必要がある場合json_encode、あなたは単にによって返され、文字列の最初のシンボルをチェックすることができますjson_encode($your_arr)-それはだかどう[{;-)
pilat

2

これが解決策になるでしょうか?

  public static function isArrayAssociative(array $array) {
      reset($array);
      return !is_int(key($array));
  }

注意点は明らかに配列カーソルがリセットされることですが、おそらく配列が走査または使用される前に関数が使用されると思います。


この関数は、返し、偽の両方のためにarray("a", "b")array("a", "b" => "B")、最初のキーそれだけチェックなど。ところで、is_longはの単なるエイリアスですis_int
2012年

1
率直に言って、これはほとんどの場合非常に効果的であり、他の方法よりもはるかに効率的だと思います。この方法の結果を理解し、それがうまくいくとわかっている場合は、おそらくこれが最良の選択です。
Gershom 2015年

これは単に間違っています。最初のキーのみを調べます。
Mark Amery

@MarkAmery質問では、純粋な順次配列を純粋に連想配列から区別する方法を尋ねました。この答えはまさにそれを行い、それらすべての中で最も効率的です。混合アレーの動作が未定義であることは、質問のコンテキストでは完全に問題ありません。+1
トビア2016

@Tobia私は、ほとんどの人が、[7 => 'foo', 2 => 'bar']部分的には「純粋に」シーケンシャルではない「混合」配列として分類することに同意するとは思わないでしょう。私にとっては、言葉の使い方が明らかに間違っているようです。
Mark Amery

2

ここでのソリューションの多くはエレガントできれいですが、十分に拡張できず、メモリ集約型またはCPU集約型です。ほとんどは、比較の両側からこのソリューションでメモリに2つの新しいデータポイントを作成しています。アレイが大きくなるほど、使用するプロセスとメモリは難しく、長くなり、短絡評価の利点が失われます。私はいくつかの異なるアイデアでいくつかのテストを行いました。コストがかかるため、array_key_existsを避け、比較する新しい大きなデータセットを作成しないようにします。これは、配列がシーケンシャルであるかどうかを確認する簡単な方法だと思います。

public function is_sequential( $arr = [] ){
    if( !is_array( $arr ) || empty( $arr ) ) return false;

    $i = 0;

    $total = count( $arr );

    foreach( $arr as $key => $value ) if( $key !== $i++ ) return false;

    return true;
}

メイン配列で単一のカウントを実行し、単一の整数を格納します。次に、配列をループして、カウンターを反復しながら正確に一致するかどうかを確認します。あなたは1から数える必要があります。失敗した場合は短絡し、falseのときにパフォーマンスが向上します。

もともと私はforループでこれを行い、isset($ arr [$ i])をチェックしていましたが、これはarray_key_existsを必要とするnullキーを検出しません。

整数サイズを超えてイテレータが成長しないことを確認するために、foreachを介して変数を常に更新します。PHPは、組み込みのメモリ最適化、キャッシュ、およびガベージコレクションを使用して、非常に低いリソース使用率を維持します。

また、単純に$ key => $ valueを実行してキーを確認できる場合、foreachでarray_keysを使用するのはばかげていると私は主張します。新しいデータポイントを作成する理由 配列キーを抽象化すると、すぐに多くのメモリを消費します。


1

答えはすでに出ていますが、パフォーマンスについての情報が多すぎます。この小さなベンチマークスクリプトを書いて、foreachメソッドが最速であることを示しました。

免責事項:以下の方法は他の回答からコピー&ペーストされました

<?php

function method_1(Array &$arr) {
    return $arr === array_values($arr);
}

function method_2(Array &$arr) {
    for (reset($arr), $i = 0; key($arr) !== $i++; next($arr));
    return is_null(key($arr));
}

function method_3(Array &$arr) {
    return array_keys($arr) === range(0, count($arr) - 1);
}

function method_4(Array &$arr) {
    $idx = 0;
    foreach( $arr as $key => $val ){
        if( $key !== $idx )
            return FALSE;
        $idx++;
    }
    return TRUE;
}




function benchmark(Array $methods, Array &$target){    
    foreach($methods as $method){
        $start = microtime(true);
        for ($i = 0; $i < 1000; $i++)
            $dummy = call_user_func($method, $target);

        $end = microtime(true);
        echo "Time taken with $method = ".round(($end-$start)*1000.0,3)."ms\n";
    }
}



$targets = [
    'Huge array' => range(0, 30000),
    'Small array' => range(0, 1000),
];
$methods = [
    'method_1',
    'method_2',
    'method_3',
    'method_4',
];
foreach($targets as $targetName => $target){
    echo "==== Benchmark using $targetName ====\n";
    benchmark($methods, $target);
    echo "\n";
}

結果:

==== Benchmark using Huge array ====
Time taken with method_1 = 5504.632ms
Time taken with method_2 = 4509.445ms
Time taken with method_3 = 8614.883ms
Time taken with method_4 = 2720.934ms

==== Benchmark using Small array ====
Time taken with method_1 = 77.159ms
Time taken with method_2 = 130.03ms
Time taken with method_3 = 160.866ms
Time taken with method_4 = 69.946ms

1

または、これを使用することもできます。

Arr::isAssoc($array)

これは、配列に数値以外のキーが含まれているどうかを確認します

Arr:isAssoc($array, true)

配列が完全にシーケンシャルであるかどうかをチェックする (自動生成されたintキー0からn-1を含む

このライブラリを使用ます。


0

PHPにその機能が組み込まれていない限り、O(n)未満でこれを行うことはできません。すべてのキーを列挙し、整数型をチェックします。実際、穴がないことも確認したいので、アルゴリズムは次のようになります。

for i in 0 to len(your_array):
    if not defined(your-array[i]):
        # this is not an array array, it's an associative array :)

しかし、なぜわざわざ?配列が期待するタイプであると想定してください。そうでない場合、それはあなたの顔に爆発するだけです-それはあなたのための動的プログラミングです!コードをテストすると、すべてがうまくいきます...


1
通常、配列が望ましいタイプであると仮定するだけでよいでしょう。しかし、私の場合、私は多次元配列をループして、指定されたノードがどのタイプの配列であるかに応じて出力をフォーマットしています。
Wilco、

0

配列のキーと配列のarray_values()の結果のキーの違いを比較します。これは常に整数インデックスを持つ配列になります。キーが同じ場合、それは連想配列ではありません。

function isHash($array) {
    if (!is_array($array)) return false;
    $diff = array_diff_assoc($array, array_values($array));
    return (empty($diff)) ? false : true;
}

-1; これは、使用してO(n)、追加のメモリ$arrayがあるnアイテムを、と書く(someboolean) ? false : true代わりにすることは!someboolean恐ろしいですし、無償で冗長。
Mark Amery
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.