PHPはすべての配列を連想配列として扱うため、組み込み関数はありません。配列に数字キーしか含まれていないかどうかを確認するためのかなり効率的な方法を推奨する人はいますか
基本的に、私はこれを区別することができるようにしたいです。
$sequentialArray = array('Apple', 'orange', 'tomato', 'carrot');
この:
$assocArray = array('fruit1' => 'Apple',
'fruit2' => 'orange',
'veg1' => 'tomato',
'veg2' => 'carrot');
まったく同じではない2つの質問をしました。
これらの動作のうち、どれが実際に必要かを検討してください。 (どちらかがあなたの目的のためにやるということかもしれません。)
最初の質問(単にすべてのキーが数字であることをチェックする)は キャプテンkurOによってよく答えられました 。
2番目の質問(配列のインデックスが0でシーケンシャルかどうかの確認)では、次の関数を使うことができます。
function isAssoc(array $arr)
{
if (array() === $arr) return false;
return array_keys($arr) !== range(0, count($arr) - 1);
}
var_dump(isAssoc(array('a', 'b', 'c'))); // false
var_dump(isAssoc(array("0" => 'a', "1" => 'b', "2" => 'c'))); // false
var_dump(isAssoc(array("1" => 'a', "0" => 'b', "2" => 'c'))); // true
var_dump(isAssoc(array("a" => 'a', "b" => 'b', "c" => 'c'))); // true
配列に非整数のキーがあるかどうかを単にチェックするには(配列がシーケンシャルインデックスかゼロインデックスかどうかは問わない):
function has_string_keys(array $array) {
return count(array_filter(array_keys($array), 'is_string')) > 0;
}
少なくとも1つの文字列キーがある場合、$array
は連想配列と見なされます。
確かにこれはより良い代替手段です。
<?php
$arr = array(1,2,3,4);
$isIndexed = array_values($arr) === $arr;
この質問の多くのコメント投稿者は、配列が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を使おうとすると、静かに ""に変換されます)。
OPで表明 :
PHPはすべての配列を連想配列として扱います
配列が連想配列であるかどうかをチェックする関数を記述することはあまり賢明ではありません(IMHO)。まず最初に: PHP配列のキーとは ?:
keyは、integerまたはstring。
つまり、次の3つのケースが考えられます。
以下の機能を使用して、各ケースを確認できます。
注:この関数は、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);
}
注:この関数は、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);
}
注:この関数は、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;
}
それは次のとおりです。
さて、配列が "genuine" arrayであるためには、誰もが慣れている、つまり:
以下の機能で確認できます。
注:この関数は、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);
}
これらの配列のキーはintegersです。
array(0 => "b");
array(13 => "b");
array(-13 => "b"); // Negative integers are also integers.
array(0x1A => "b"); // Hexadecimal notation.
これらの配列のキーは、stringsです。
array("fish and chips" => "b");
array("" => "b"); // An empty string is also a string.
array("[email protected]" => "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")
のキーがstringであると思う場合、間違っています。ドキュメントから here :
有効な整数を含む文字列は、整数型にキャストされます。例えば。キー「8」は実際には8の下に格納されます。一方、「08」は有効な10進整数ではないため、キャストされません。
たとえば、これらの配列のキーはintegersです。
array("13" => "b");
array("-13" => "b"); // Negative, ok.
しかし、これらの配列のキーはstringsです:
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.
さらに、 doc によると、
整数のサイズはプラットフォームに依存しますが、最大値である約20億が通常の値です(32ビットの符号付き)。通常、64ビットプラットフォームの最大値は約9E18です。ただし、Windowsは常に32ビットです。 PHPは符号なし整数をサポートしていません。
したがって、この配列のキーは、integerであってもなくてもよい-プラットフォームによって異なります。
array("60000000000" => "b"); // Array key could be integer or string, it can fit into a 64-bit (but not 32-bit) integer.
さらに悪いことに、整数が2に近い場合、PHPはbuggyになる傾向があります。31 = 2,147,483,648境界( バグ514 、 バグ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"
}
したがって、キーは、ある環境ではintegerですが、別の環境ではstringです。たとえ2147483647
が有効な署名付きであっても32ビット整数。
スピードに関して:
function isAssoc($array)
{
return ($array !== array_values($array));
}
メモリに関して:
function isAssoc($array)
{
$array = array_keys($array); return ($array !== array_keys($array));
}
function checkAssoc($array){
return ctype_digit( implode('', array_keys($array) ) );
}
私はarray_keys($obj) !== range(0, count($obj) - 1)
とarray_values($arr) !== $arr
の両方を使用しました(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;
}
また、疎な配列と連想配列を区別したくない場合は、両方のif
ブロックから'assoc'
を返すことができます。
最後に、これはこのページの多くの「ソリューション」よりもはるかに「エレガント」ではないように見えるかもしれませんが、実際には非常に効率的です。ほとんどすべての連想配列が即座に検出されます。インデックス付きの配列だけが徹底的にチェックされ、上記のメソッドはインデックス付きの配列を徹底的にチェックするだけでなく、それらを複製します。
次の2つの関数は、「配列が連想型か数値型か」をチェックするのに最適な方法だと思います。 'numeric'は数字キーだけ、またはシーケンシャル数字キーだけを意味するので、どちらかの条件をチェックする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なので、ベース値を指定する必要がない場合は省略できます。キー($ my_array)は、読み取りポインタが配列の末尾を超えて移動した場合はnullを返します。これにより、forループは終了し、すべてのキーが整数の場合はforループの後のステートメントはtrueを返します。そうでない場合、キーは文字列型であるため、ループは途中で終了し、forループの後のステートメントはfalseを返します。さらに後者の関数は、比較のたびに$ baseに1を加えて、次のキーが正しい値であるかどうかをチェックできるようにします。厳密な比較では、キーが整数型かどうかもチェックします。 forループの最初のセクションにある$ base =(int)$ baseの部分は、$ baseが省略されている場合、または整数を使用してのみ呼び出されるようにした場合は、省略できます。しかし、私は誰にとっても確信が持てないので、そのままにしました。とにかく、ステートメントは一度だけ実行されます。私はこれらが最も効率的な解決策だと思います:
配列キーは整数または文字列のみであり、 "1"などの厳密に数値の文字列(ただし "01"は不可)は整数に変換されます。これが、配列をシーケンシャルにしたい場合のカウント以外に、整数キーのチェックに必要な唯一の操作です。当然、is_indexed_arrayがfalseを返す場合、配列は連想配列と見なすことができます。私は「見た」と言っています、実際にはそれらはすべてそうです。
この質問に対する2つの一般的なアプローチに気付きました。1つはarray_values()
を使うもの、もう1つは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()
を使うほうがはるかに速いです。
この関数は処理することができます:
考え方は簡単です。キーの1つが整数でなければ連想配列で、そうでなければ順次です。
function is_asso($a){
foreach(array_keys($a) as $key) {if (!is_int($key)) return TRUE;}
return FALSE;
}
これに取り組む1つの方法はjson_encode
に便乗することです。これは正しいJSONを出力するために既に連想配列とインデックス付き配列を区別する独自の内部メソッドを持っています。
これは、エンコード後に返される最初の文字が{
(連想配列)か[
(インデックス付き配列)かを確認することで確認できます。
// Too short :)
function is_assoc($arr) {
ksort($arr);
return json_encode($arr)[0] === '{';
}
function array_is_assoc(array $a) {
$i = 0;
foreach ($a as $k => $v) {
if ($k !== $i++) {
return true;
}
}
return false;
}
高速、簡潔、そしてメモリ効率が良い。高価な比較、関数呼び出し、配列のコピーは不要です。
すでに多くの答えがありますが、これは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/Arrr.php
xarray PHP拡張子を使用する
あなたはこれを非常に速くすることができます(PHP 5.6では約30倍も速い):
if (array_is_indexed($array)) { }
または
if (array_is_assoc($array)) { }
私の解決策:
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からインデックスが再作成されます。
これが私が使う方法です:
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
私はそれがこの巨大なキューに答えを追加することに少し意味がないことを知っています、しかしここに値を複製する必要がない読みやすいO(n)解決策があります:
function isNumericArray($array) {
$count = count($array);
for ($i = 0; $i < $count; $i++) {
if (!isset($array[$i])) {
return FALSE;
}
}
return TRUE;
}
キーがすべて数値であるかどうかを確認するのではなく、数値配列に対してwouldが存在することを確認し、それらが存在することを確認します。
スカラ配列の定義はアプリケーションによって異なると思います。つまり、アプリケーションによっては、スカラー配列と見なされるものについてより厳密な意味を必要とする場合があります。また、アプリケーションによっては、より緩い意味を必要とする場合があります。
以下に、厳密さを変える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 <[email protected]>
*/
/**
* 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;
}
いくつかのローカルベンチマーク、デバッグ、コンパイラプロービング、プロファイリング、そして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;
}
<?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";
}
これもうまくいくでしょう( demo ):
function array_has_numeric_keys_only(array $array)
{
try {
SplFixedArray::fromArray($array, true);
} catch (InvalidArgumentException $e) {
return false;
}
return true;
}
この答えの主なポイントはSplFixedArray
の存在についてあなたに知らせることであり、あなたがこれらの種類のテストにExceptionsを使うことを奨励することではないことに注意してください。
これは解決策でしょうか。
public static function isArrayAssociative(array $array) {
reset($array);
return !is_int(key($array));
}
警告は明らかに配列カーソルがリセットされることですが、私はおそらく配列が横断されるか使用される前に関数が使用されると思います。
<?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)
のような配列では正しく機能しません。
source からもう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__)
);
}
}
答えはすでに与えられていますが、パフォーマンスに関してあまりにも多くの虚偽情報があります。私は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
私の意見では、そのキーのいずれかが整数ではない場合、配列は連想として受け入れられるべきです。浮動小数点数と空の文字列 ''。
このようにしてこれらの種類の配列をforループと一緒に使用することはできないため、順序付けされていない整数も(0,2,4,6)のように結合的と見なす必要があります。
$n =count($arr);
for($i=0,$i<$n;$i++)
以下の関数の2番目の部分は、キーがインデックスされているかどうかをチェックします。負の値を持つキーに対しても機能します。例えば(-1,0,1,2,3,4,5)
count() = 7 , max = 5, min=-1
if( 7 == (5-(-1)+1 ) // true
return false; // array not associative
/**
* isAssoc Checks if an array is associative
* @param $arr reference to the array to be checked
* @return bool
*/
function IsAssoc(&$arr){
$keys= array_keys($arr);
foreach($keys as $key){
if (!is_integer($key))
return true;
}
// if all keys are integer then check if they are indexed
if(count($arr) == (max($keys)-min($keys)+1))
return false;
else
return true;
}
ここでの解決策の多くはエレガントできれいですが、うまく拡張できず、メモリ集約型または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から数える必要があります。失敗すると、回路が短絡し、偽の場合にパフォーマンスが向上します。
もともと私はforループとisset($ arr [$ i])をチェックしてこれを行いましたが、これはarray_key_existsを必要とするnullキーを検出しません。これは速度のために使用する最悪の関数です。
Foreachを介して変数を常に更新して、整数サイズを超えないイテレータと一緒にチェックするPHP組み込みのメモリ最適化、キャッシュ、ガベージコレクションを使用して、リソース使用量を非常に低く抑えます。
また、foreachでarray_keysを使用することは、単に$ key => $ valueを実行してキーをチェックできる場合は愚かであると主張します。新しいデータポイントを作成する理由配列キーを抽象化すると、すぐにメモリを消費します。
私は次の方法を思い付きました:
function isSequential(array $list): bool
{
$i = 0;
$count = count($list);
while (array_key_exists($i, $list)) {
$i += 1;
if ($i === $count) {
return true;
}
}
return false;
}
var_dump(isSequential(array())); // false
var_dump(isSequential(array('a', 'b', 'c'))); // true
var_dump(isSequential(array("0" => 'a', "1" => 'b', "2" => 'c'))); // true
var_dump(isSequential(array("1" => 'a', "0" => 'b', "2" => 'c'))); // true
var_dump(isSequential(array("1a" => 'a', "0b" => 'b', "2c" => 'c'))); // false
var_dump(isSequential(array("a" => 'a', "b" => 'b', "c" => 'c'))); // false
*空の配列はシーケンシャル配列とは見なされませんが、空の配列は0のようなものなので問題ないと思います。プラスまたはマイナスの関係なく、空です。
上記のものと比較したこの方法の利点は次のとおりです。
array_values
はコピーを含みません - 何!? 確かにそうです - 意志として下に見られる )Artur Bodera から提供されたベンチマークを使用しました。ここでは、配列の1つを1M要素に変更しました(array_fill(0, 1000000, uniqid()), // big numeric array
)。
これが100回の反復の結果です。
PHP 7.1.16 (cli) (built: Mar 31 2018 02:59:59) ( NTS )
Initial memory: 32.42 MB
Testing my_method (isset check) - 100 iterations
Total time: 2.57942 s
Total memory: 32.48 MB
Testing method3 (array_filter of keys) - 100 iterations
Total time: 5.10964 s
Total memory: 64.42 MB
Testing method1 (array_values check) - 100 iterations
Total time: 3.07591 s
Total memory: 64.42 MB
Testing method2 (array_keys comparison) - 100 iterations
Total time: 5.62937 s
Total memory: 96.43 MB
*メソッドはメモリ消費量に基づいて順序付けされています
**メモリ使用量を表示するためにecho " Total memory: " . number_format(memory_get_peak_usage()/1024/1024, 2) . " MB\n";
を使いました
function is_array_assoc($foo) {
if (is_array($foo)) {
return (count(array_filter(array_keys($foo), 'is_string')) > 0);
}
return false;
}
配列のキーと配列のarray_values()の結果のキーの違いを比較します。これは常に整数のインデックスを持つ配列になります。キーが同じ場合は、連想配列ではありません。
function isHash($array) {
if (!is_array($array)) return false;
$diff = array_diff_assoc($array, array_values($array));
return (empty($diff)) ? false : true;
}
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 :)
しかし、なぜ迷惑なのですか?配列が期待通りの型であると仮定してください。そうでなければ、それはちょうどあなたの顔に爆発するでしょう - それはあなたのための動的プログラミングです!あなたのコードをテストすれば、すべてうまくいくでしょう...
function is_associative($arr) {
return (array_merge($arr) !== $arr || count(array_filter($arr, 'is_string', ARRAY_FILTER_USE_KEY)) > 0);
}
これを行うさらに別の方法。
function array_isassociative($array)
{
// Create new Array, Make it the same size as the input array
$compareArray = array_pad(array(), count($array), 0);
// Compare the two array_keys
return (count(array_diff_key($array, $compareArray))) ? true : false;
}
/*
iszba - Is Zero Based Array
Detects if an array is zero based or not.
PARAMS:
$chkvfnc
Callback in the loop allows to check the values of each element.
Signature:
bool function chkvfnc($v);
return:
true continue looping
false stop looping; iszba returns false too.
NOTES:
○ assert: $array is an array.
○ May be memory efficient;
it doesn't get extra arrays via array_keys() or ranges() into the function.
○ Is pretty fast without a callback.
○ With callback it's ~2.4 times slower.
*/
function iszba($array, $chkvfnc=null){
$ncb = !$chkvfnc;
$i = 0;
foreach($array as $k => $v){
if($k === $i++)
if($ncb || $chkvfnc($v))
continue;
return false;
}
return true;
}
•コールバックなしで、それは現在の主な答えよりも〜30%速く、そしておそらくもっとメモリ効率的です。
•配列を連想型と見なすべきかどうかを知るために答えを否定するだけです。
あるいは、これを使うこともできます。
Arr::isAssoc($array)
これは配列に 数値以外のキーが含まれているかどうかをチェックします または:
Arr:isAssoc($array, true)
配列 が厳密に連続しているかどうかをチェックする (自動生成されたintキーからn-1を含む)
this libraryを使用してください。
最も人気のある答えの修正。
これはもう少し処理がかかりますが、より正確です。
<?php
//$a is a subset of $b
function isSubset($a, $b)
{
foreach($a =>$v)
if(array_search($v, $b) === false)
return false;
return true;
//less effecient, clearer implementation. (uses === for comparison)
//return array_intersect($a, $b) === $a;
}
function isAssoc($arr)
{
return !isSubset(array_keys($arr), range(0, count($arr) - 1));
}
var_dump(isAssoc(array('a', 'b', 'c'))); // false
var_dump(isAssoc(array(1 => 'a', 0 => 'b', 2 => 'c'))); // false
var_dump(isAssoc(array("0" => 'a', "1" => 'b', "2" => 'c'))); // false
//(use === in isSubset to get 'true' for above statement)
var_dump(isAssoc(array("a" => 'a', "b" => 'b', "c" => 'c'))); // true
?>