如何检查PHP数组是关联的还是顺序的?

如何检查PHP数组是关联的还是顺序的?,php,arrays,Php,Arrays,PHP将所有数组视为关联数组,因此没有任何内置函数。有人能推荐一种相当有效的方法来检查数组是否只包含数字键吗 基本上,我希望能够区分这两种情况: $sequentialArray = [ 'apple', 'orange', 'tomato', 'carrot' ]; 这是: $assocArray = [ 'fruit1' => 'apple', 'fruit2' => 'orange', 'veg1' => 'tomato', 'v

PHP将所有数组视为关联数组,因此没有任何内置函数。有人能推荐一种相当有效的方法来检查数组是否只包含数字键吗

基本上,我希望能够区分这两种情况:

$sequentialArray = [
    'apple', 'orange', 'tomato', 'carrot'
];
这是:

$assocArray = [
    'fruit1' => 'apple',
    'fruit2' => 'orange',
    'veg1' => 'tomato',
    'veg2' => 'carrot'
];

您提出了两个不完全相同的问题:

  • 首先,如何确定数组是否只有数字键
  • 其次,如何确定数组是否具有从0开始的顺序数字键
考虑一下您实际需要哪些行为。(可能这两种方法都适用于您的目的。)

第一个问题(简单地检查所有键是否都是数字)是

对于第二个问题(检查数组是否为零索引和顺序),可以使用以下函数:

函数isAssoc(数组$arr)
{
if(array()==$arr)返回false;
返回数组_键($arr)!==范围(0,计数($arr)-1);
}
变量转储(isAssoc(['a','b','c']);//假的
变量转储(isAssoc([“0”=>“a”、“1”=>“b”、“2”=>“c”]);//假的
变量转储(isAssoc([“1”=>“a”、“0”=>“b”、“2”=>“c”]);//真的
变量转储(isAssoc([“a”=>“a”、“b”=>“b”、“c”=>“c”]);//真的

这肯定是一个更好的选择

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

实际上,最有效的方法是:

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

这是因为它比较了键(对于顺序数组,总是0,1,2等)和键的键(总是0,1,2等)。

我使用了两个
数组_键($obj)!=范围(0,计数($obj)-1)
数组_值($arr)!=$arr
(虽然第二个比第一个便宜,但它们彼此都是对偶的),但对于非常大的阵列,两者都失败

这是因为
array\u键
array\u值
都是非常昂贵的操作(因为它们构建了一个大小与原始数组大致相同的全新数组)

与上述方法相比,以下函数更稳健:

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;
}
函数数组类型($obj){ $last_key=-1; $type='index'; foreach($obj as$key=>$val){ 如果(!is_int($key)|$key<0){ 返回“assoc”; } 如果($key!==$last_key+1){ $type='sparse'; } $last_key=$key; } 返回$type; } 还请注意,如果不想区分稀疏数组和关联数组,只需从两个
if
块返回
'assoc'


最后,虽然这看起来不像本页上的许多“解决方案”那么“优雅”,但实际上它的效率要高得多。几乎任何关联数组都会立即被检测到。只有索引数组才会被彻底检查,上述方法不仅会彻底检查索引数组,还会复制它们。

此函数可以处理:

  • 索引中有孔的阵列(例如1,2,4,5,8,10)
  • 具有“0x”键的数组:例如,键“08”是关联的,而键“8”是顺序的
其思想很简单:如果其中一个键不是整数,则它是关联数组,否则它是顺序数组

function is_asso($a){
    foreach(array_keys($a) as $key) {if (!is_int($key)) return TRUE;}
    return FALSE;
}
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;
}

要仅检查数组是否具有非整数键(而不是数组是顺序索引还是零索引):


如果至少有一个字符串键,
$array
将被视为关联数组。

这是解决方案吗

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

很明显,需要注意的是数组光标被重置了,但我想说的是,可能在遍历或使用数组之前就已经使用了该函数。

这个问题中的许多评论者不理解数组在PHP中是如何工作的。从:

键可以是整数或字符串。如果一个键是一个整数的标准表示形式,它将被解释为这样(即,“8”将被解释为8,而“08”将被解释为“08”)。键中的浮点被截断为整数。索引数组类型和关联数组类型在PHP中是相同的类型,可以包含整数和字符串索引

换句话说,数组键“8”是不存在的,因为它总是(静默地)转换为整数8。因此,尝试区分整数和数字字符串是不必要的

如果您希望以最有效的方式检查数组中的非整数键,而无需复制数组的一部分(如array_keys()所做的)或全部(如foreach所做的),请执行以下操作:


这是因为key()在当前数组位置无效时返回NULL,并且NULL永远不能是有效的键(如果尝试将NULL用作数组键,它会被静默转换为“”。

我使用的方法如下:

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

速度方面:

function isAssoc($array)
{
    return ($array !== array_values($array));
}
function isAssoc($array)
{
    $array = array_keys($array); return ($array !== array_keys($array));
}
内存方面:

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

这两个得分最高的示例都不能正确地使用数组,例如
$array=array('foo'=>'bar',1)
这也适用():


请注意,此答案的要点是通知您是否存在
SplFixedArray
,而不是鼓励您在此类测试中使用异常。

我认为以下两个函数是检查“数组是关联的还是数字的”的最佳方法。由于“numeric”可能仅表示数字键或顺序数字键,下面列出了两个用于检查任一条件的函数:

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));
}
第一个函数检查每个键是否为整数值。第二个函数检查每个键是否为整数值,此外还检查所有键是否从$base开始按顺序排列,该值默认为0,因此,如果不需要指定另一个基值,则可以忽略该值。如果读取的po
function array_has_numeric_keys_only(array $array)
{
    try {
        SplFixedArray::fromArray($array, true);
    } catch (InvalidArgumentException $e) {
        return false;
    }
    return true;
}
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));
}
//! 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);
}
//! 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);
}
//! 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;
}
//! 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);
}
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");
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.
array("60000000000" => "b");                // Array key could be integer or string, it can fit into a 64-bit (but not 32-bit) integer.
array(1) {
    [2147483647]=>
    string(1) "b"
}   
array(1) {
    ["2147483647"]=>
    string(1) "b"
}
<?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;
}
$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";
function isNumericArray($array) {
    $count = count($array);
    for ($i = 0; $i < $count; $i++) {
        if (!isset($array[$i])) {
            return FALSE;
        }
    }
    return TRUE;
}
function isAssociative(array $array)
{
    return array_keys(array_merge($array)) !== range(0, count($array) - 1);
}
array_merge([1 => 'One', 3 => 'Three', 'two' => 'Two', 6 => 'Six']);

// This will returns [0 => 'One', 1 => 'Three', 'two' => 'Two', 2 => 'Six']
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__)
        );
    }
}
if (array_is_indexed($array)) {  }
if (array_is_assoc($array)) {  }
// 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;
}
<?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
/**
 * 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;
}
Arr::isAssoc($array)
Arr:isAssoc($array, true)
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;
}