Làm thế nào để kiểm tra xem mảng PHP là liên kết hay tuần tự?


781

PHP coi tất cả các mảng là liên kết, do đó không có bất kỳ hàm dựng sẵn nào. Bất cứ ai cũng có thể đề xuất một cách khá hiệu quả để kiểm tra nếu một mảng chỉ chứa các khóa số?

Về cơ bản, tôi muốn có thể phân biệt giữa điều này:

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

và điều này:

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

382
Có một lỗi trong mã của bạn: Cà chua là một loại trái cây.
Olle Härstedt

9
Phương pháp này có sự cẩn thận, nhưng tôi thường chỉ làm if (isset($array[0])), rất đơn giản và nhanh chóng. Tất nhiên, trước tiên bạn nên chắc chắn rằng mảng không trống và bạn nên có một số kiến ​​thức về các nội dung có thể có của mảng để phương thức không thể thất bại (như hỗn hợp số / kết hợp hoặc không tuần tự).
Gras Double

@ OlleHärstedt Không theo Tòa án tối cao Hoa Kỳ. ;-)
MC Hoàng đế

Câu trả lời:


622

Bạn đã hỏi hai câu hỏi không hoàn toàn tương đương:

  • Đầu tiên, làm thế nào để xác định xem một mảng chỉ có các phím số
  • Thứ hai, làm thế nào để xác định xem một mảng có các khóa số liên tiếp hay không , bắt đầu từ 0

Xem xét những hành vi mà bạn thực sự cần. (Có thể là một trong hai sẽ làm cho mục đích của bạn.)

Câu hỏi đầu tiên (đơn giản là kiểm tra xem tất cả các khóa có phải là số không) được Captain KurO trả lời tốt .

Đối với câu hỏi thứ hai (kiểm tra xem mảng có được lập chỉ mục bằng 0 và tuần tự không), bạn có thể sử dụng hàm sau:

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
Giải pháp rất thanh lịch. Lưu ý rằng nó trả về TRUE trong trường hợp (mơ hồ) của một mảng trống.
Jonathan Lidbeck

30
Tôi nghĩ sẽ hữu ích hơn khi nghĩ về các mảng liên tiếp như là một trường hợp đặc biệt của các mảng kết hợp. Vì vậy, mỗi mảng là kết hợp, nhưng chỉ một số là tuần tự. Do đó, một chức năng isSequential()sẽ có ý nghĩa hơn isAssoc(). Trong một hàm như vậy, mảng trống nên được xem là tuần tự. Công thức có thể là array() === $arr || !isAssoc($arr).
donquixote

18
Tôi nghĩ rằng điều này sẽ tránh được rất nhiều thời gian và bộ nhớ cpu tiềm năng nếu người ta kiểm tra xem nếuetet ($ Array [0]) là sai trước khi trích xuất tất cả các khóa vì nó có liên quan rõ ràng nếu mảng không trống nhưng không có phần tử nào trong 0 Chức vụ. Vì "hầu hết" các mảng kết hợp thực sự có các chuỗi như các khóa nên đây là một tối ưu hóa tốt cho trường hợp chung của hàm đó.
OderWat

10
@OderWat - Tối ưu hóa của bạn nên sử dụng array_key_existsthay issetvì bởi vì nếu phần tử zero là giá trị null, thìetet sẽ trả về sai. Một giá trị null thường là một giá trị hợp pháp trong một mảng như vậy.
OCDev

@MAChitgarha, bản chỉnh sửa của bạn đã thay đổi hành vi của chức năng mà không có bất kỳ lời giải thích nào về lý do và khiến nó mâu thuẫn với mô tả trong văn xuôi ở trên về những gì nó thực sự phải làm. Tôi đã hoàn nguyên nó.
Đánh dấu Amery

431

Để chỉ kiểm tra xem mảng có các khóa không nguyên (không phải là mảng được lập chỉ mục tuần tự hay không có chỉ mục):

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

Nếu có ít nhất một khóa chuỗi, $arraysẽ được coi là một mảng kết hợp.


22
Phương pháp này tốt hơn nhiều so với nó có vẻ. Nếu Count (filtered_array) == Count (original_array), thì đó là một mảng PGS. Nếu tính (filtered_array) == 0, thì đó là một mảng được lập chỉ mục. Nếu Count (filtered_array) <Count (original_array), thì mảng có cả khóa số và khóa chuỗi.
Jamol

5
@MikePretzlaw tất nhiên nó lặp đi lặp lại; (rõ ràng) không có cách nào khả thi để xác định xem tất cả các khóa của mảng có phải là int không mà không cần nhìn vào tất cả các khóa trong mảng. Tôi giả sử các lựa chọn thay thế không lặp đi lặp lại mà chúng ta sẽ thấy dưới đây là những lựa chọn như thế $isIndexed = array_values($arr) === $arr;nào? Tôi hỏi: bạn nghĩ nó array_values()hoạt động như thế nào? Làm thế nào để bạn nghĩ rằng ===áp dụng cho mảng hoạt động? Câu trả lời tất nhiên là họ cũng lặp lại trên mảng.
Đánh dấu Amery

4
@ARW "PHP dường như chuyển mọi thứ thành int trong một định nghĩa mảng nếu có thể." - vâng, đó chính xác là những gì xảy ra. WTF lớn nhất là nó thậm chí còn làm điều này để nổi; nếu bạn thử var_dump([1.2 => 'foo', 1.5 => 'bar']);bạn sẽ khám phá ra rằng bạn có được mảng [1 => 'bar']. Không có cách nào để tìm ra loại ban đầu của khóa. Vâng, tất cả điều này là khủng khiếp; Các mảng của PHP cho đến nay là phần tồi tệ nhất của ngôn ngữ và phần lớn thiệt hại là không thể khắc phục được và nợ ý tưởng sử dụng một cấu trúc duy nhất cho các mảng truyền thống và các hashtag truyền thống là một điều tồi tệ ngay từ đầu.
Đánh dấu Amery

30
@MarkAmery Ở trên, trong khi đơn giản, đảm bảo 100% đi bộ của mảng. Sẽ hiệu quả hơn, đặc biệt là nếu bạn đang xử lý các mảng lớn, nếu bạn đang kiểm tra chuỗi hoặc int và đã xảy ra vào lần đầu tiên bạn tìm thấy. Ví dụ: function isAssociative($arr) { foreach ($arr as $key => $value) { if (is_string($key)) return true; } return false; }
Nghĩ

1
@ Suy nghĩ Mã của bạn hoạt động rất nhanh nhưng nó không thể phát hiện mảng liên tiếp . Ví dụ array(1 => 'a', 0 => 'b', 2 => 'c')sẽ trở thành false(mảng tuần tự) trong khi nó phải là true(mảng kết hợp). toolsqa.com/data-strucenses/array-in-programming Tôi không chắc khóa có phải là thứ tự tăng dần không? (0, 1, ...)
vee

132

Chắc chắn đây là một sự thay thế tốt hơn.

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

52
Điều này sẽ nhân đôi các giá trị trong mảng, có khả năng rất tốn kém. Bạn tốt hơn nhiều khi kiểm tra các phím mảng.
meagar

8
Tôi vừa mới sử dụng ==; Tôi không nghĩ cần có === ở đây. Nhưng để trả lời "unset và nó không hoạt động": một khi bạn bỏ đặt phần tử đầu tiên, nó không còn là một mảng được lập chỉ mục số nguyên bắt đầu từ 0. Vì vậy, IMO nó hoạt động.
Grantwparks

4
Đồng ý với @grantwparks: Một mảng thưa thớt không được lập chỉ mục. Thật thú vị bởi vì không có cách nào để thực sự xóa một phần tử ra giữa một mảng được lập chỉ mục PHP về cơ bản là khai báo tất cả các mảng là liên kết và số chỉ là một phiên bản 'tạo nên chìa khóa cho tôi'.
RickMeasham

7
Vấn đề duy nhất tôi gặp phải là điều này ===sẽ lãng phí thời gian kiểm tra xem các giá trị có bằng nhau hay không, mặc dù chúng tôi chỉ quan tâm đến các khóa. Vì lý do này, tôi thích $k = array_keys( $arr ); return $k === array_keys( $k );phiên bản.
Jesse

5
Một ghi chú được thêm vào, điều này không thành công trên các mảng được chỉ định với các phím số không theo thứ tự. ví dụ: $ myArr = mảng (0 => 'a', 3 => 'b', 4 => 1, 2 => 2, 1 => '3'); Một công việc tiềm năng xung quanh là chạy ksort ($ Array) trước khi thực hiện bài kiểm tra
Scott

77

Nhiều người bình luận trong câu hỏi này không hiểu cách các mảng hoạt động trong PHP. Từ tài liệu mảng :

Khóa có thể là số nguyên hoặc chuỗi. Nếu một khóa là biểu diễn tiêu chuẩn của một số nguyên, nó sẽ được hiểu như vậy (tức là "8" sẽ được hiểu là 8, trong khi "08" sẽ được hiểu là "08"). Phao trong khóa được cắt ngắn thành số nguyên. Các kiểu mảng được lập chỉ mục và liên kết là cùng loại trong PHP, cả hai đều có thể chứa các chỉ số nguyên và chuỗi.

Nói cách khác, không có thứ gọi là khóa mảng "8" bởi vì nó sẽ luôn luôn được chuyển đổi (âm thầm) thành số nguyên 8. Vì vậy, cố gắng phân biệt giữa các số nguyên và chuỗi số là không cần thiết.

Nếu bạn muốn cách hiệu quả nhất để kiểm tra một mảng cho các khóa không nguyên mà không tạo một bản sao của một phần của mảng (như Array_keys () hiện) hoặc tất cả của nó (như foreach hiện):

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

Điều này hoạt động vì khóa () trả về NULL khi vị trí mảng hiện tại không hợp lệ và NULL không bao giờ có thể là khóa hợp lệ (nếu bạn cố gắng sử dụng NULL làm khóa mảng, nó sẽ được chuyển đổi âm thầm thành "").


Điều này không làm việc cho các khóa số nguyên không tuần tự. Hãy thử với [2 => 'a', 4 => 'b'].
DavidJ

2
@DavidJ, ý của bạn là "không hoạt động" là gì? Nó xác định thành công rằng tất cả các khóa là số nguyên. Bạn có cho rằng một mảng giống như mảng bạn đã đăng không nên được coi là "mảng số" không?
coredumperror

7
Một mảng không liên kết phải có các khóa từ 0đến count($array)-1, theo thứ tự nghiêm ngặt này. Một kiểm tra sơ bộ với is_array()có thể giúp đỡ. Thêm một biến tăng để kiểm tra chuỗi khóa: for ($k = 0, reset($array) ; $k === key($array) ; next($array)) ++$k;Điều đó giải quyết thỏa thuận.
ofavre

2
Sử dụng foreachthay vì lặp lại rõ ràng là nhanh hơn khoảng hai lần.
ofavre

1
Nếu bạn muốn biến điều này thành một chức năng: function isAssocStr($array) { for (reset($array); is_int(key($array)); next($array)) { if (is_null(key($array))) return false; } return true; }
GreeKatrina

39

Như tuyên bố của OP :

PHP coi tất cả các mảng là liên kết

Nó không hoàn toàn hợp lý (IMHO) để viết một hàm kiểm tra xem một mảng có liên quan hay không . Vì vậy, điều đầu tiên trước tiên: khóa trong mảng PHP là gì ?:

Các chính hoặc có thể là một số nguyên hoặc một chuỗi .

Điều đó có nghĩa là có 3 trường hợp có thể xảy ra:

  • Trường hợp 1. tất cả các khóa là số / số nguyên .
  • Trường hợp 2. tất cả các khóa là chuỗi .
  • Trường hợp 3. một số khóa là chuỗi , một số khóa là số / số nguyên .

Chúng ta có thể kiểm tra từng trường hợp với các chức năng sau.

Trường hợp 1: tất cả các khóa là số / số nguyên .

Lưu ý : Hàm này cũng trả về true cho các mảng trống.

//! 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);
}

Trường hợp 2: tất cả các khóa là chuỗi .

Lưu ý : Hàm này cũng trả về true cho các mảng trống.

//! 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);
}

Trường hợp 3. một số khóa là chuỗi , một số khóa là số / số nguyên .

Lưu ý : Hàm này cũng trả về true cho các mảng trống.

//! 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;
}

Nó theo sau:


Bây giờ, để một mảng là một mảng "chính hãng" mà tất cả chúng ta đều quen thuộc, có nghĩa là:

  • Các khóa của nó là tất cả số / số nguyên .
  • Các khóa của nó là tuần tự (tức là tăng theo bước 1).
  • Các phím của nó bắt đầu từ số không .

Chúng ta có thể kiểm tra với chức năng sau.

Trường hợp 3a. các khóa là số / số nguyên , tuần tựkhông dựa trên .

Lưu ý : Hàm này cũng trả về true cho các mảng trống.

//! 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);
}

Hãy cẩn thận / Cạm bẫy (hoặc, thậm chí nhiều sự thật đặc biệt hơn về các khóa mảng trong PHP)

Phím số nguyên

Các khóa cho các mảng này là số nguyên :

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

Phím chuỗi

Các khóa cho các mảng này là các chuỗi :

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!

Các khóa số nguyên trông giống như các chuỗi

Nếu bạn nghĩ rằng khóa trong array("13" => "b")là một chuỗi , bạn đã sai . Từ tài liệu ở đây :

Các chuỗi chứa số nguyên hợp lệ sẽ được chuyển sang loại số nguyên. Ví dụ, khóa "8" sẽ thực sự được lưu trữ dưới 8. Mặt khác, "08" sẽ không được sử dụng, vì đó không phải là số nguyên thập phân hợp lệ.

Ví dụ, khóa cho các mảng này là số nguyên :

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

Nhưng chìa khóa cho các mảng này là các chuỗi :

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.

Hơn nữa, theo tài liệu ,

Kích thước của một số nguyên phụ thuộc vào nền tảng, mặc dù giá trị tối đa khoảng hai tỷ là giá trị thông thường (đó là 32 bit được ký). Các nền tảng 64 bit thường có giá trị tối đa khoảng 9E18, ngoại trừ Windows, luôn là 32 bit. PHP không hỗ trợ số nguyên không dấu.

Vì vậy, khóa cho mảng này có thể có hoặc không phảisố nguyên - nó phụ thuộc vào nền tảng của bạn.

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

Thậm chí tệ hơn, PHP có xu hướng bị lỗi nếu số nguyên ở gần ranh giới 2 31 = 2.147.483.648 (xem lỗi 51430 , lỗi 52899 ). Ví dụ: trên môi trường cục bộ của tôi (PHP 5.3.8 trên XAMPP 1.7.7 trên Windows 7), var_dump(array("2147483647" => "b"))sẽ cho

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

nhưng trên bản demo trực tiếp này trên codepad (PHP 5.2.5), cùng một biểu thức cho

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

Vì vậy, khóa là một số nguyên trong một môi trường nhưng một chuỗi trong một môi trường khác, mặc dù 2147483647là một số nguyên 32 bit được ký hợp lệ .


2
Ngoại trừ, như tôi đã đề cập dưới đây, nó liên quan đến việc tạo ra một mảng trùng lặp với mảng đang được kiểm tra, làm cho nó rất tốn kém cho các mảng lớn và là nguồn gây ra sự cố bộ nhớ trên các máy chủ được chia sẻ.
podperson

35

Tốc độ khôn ngoan:

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

Trí nhớ khôn ngoan:

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

mảng sau: mảng (02 => 11,1,2,456); được hiển thị là không có khóa số bằng thuật toán trên, ngay cả khi 02 === 2
Galileo_Galilei

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

2
Đây là câu trả lời duy nhất (tại thời điểm nhận xét của tôi) có thể giải quyết các vấn đề sau: $ Array = mảng (0 => 'blah', 2 => 'yep', 3 => 'wahey')
Shabbycoat

nhưng array('1'=>'asdf', '2'=>'too')sẽ được coi là mảng kết hợp trong khi thực tế thì không (các khóa thực sự là chuỗi)
Thuyền trưởng KurO

1
@CaptainkurO Ý bạn là số. Nó là một mảng kết hợp.
devios1

1
Hàm này trả về truenếu các khóa là: zero, số nguyên (chỉ dương), một chuỗi trống hoặc bất kỳ kết hợp nào ở trên, chẳng hạn như chuỗi "09". Chức năng này không đưa thứ tự của các khóa vào tài khoản. Vì vậy array(0=>'blah', 2=>'yep', 3=>'wahey'), array(0=>'blah', 2=>'yep', 1=>'wahey')array('blah', 'yep', 'wahey')tất cả đều liên kết theo chức năng này, trong khi array('a'=>'blah', 'b'=>'yep', 'c'=>'wahey')không.
Pang

@CaptainkurO bạn không chính xác. '1' và '2' sẽ được lưu dưới dạng số nguyên. Đọc phần trích dẫn của câu trả lời của sóc từ ngày 11 tháng 5 năm 2011 lúc 19:34. PHP không lưu trữ các khóa chuỗi trông giống hệt như số nguyên. Nó chuyển đổi chúng thành số nguyên.
Butussy Butkus

20

Trên thực tế, cách hiệu quả nhất là:

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

Điều này hoạt động vì nó so sánh các khóa (mà đối với một mảng tuần tự luôn là 0,1,2, v.v.) với các phím của các phím (sẽ luôn là 0,1,2, v.v.).


1
Khéo léo, nhưng không tốt. Tại sao điều này "hiệu quả nhất"? Sẽ dễ đọc hơn rất nhiều nếu chỉ so sánh mảng_key ($ a) với phạm vi (0, đếm ($ a)). Giải pháp thông minh nhất hiếm khi là giải pháp tốt nhất trong kinh nghiệm của tôi. Đặc biệt là khi thông minh bổ sung theo nghĩa đen không có giá trị hơn sự thay thế rõ ràng và rõ ràng.
Shane H

4
Hàm này trả về truecho array(1=>"a")nhưng falsecho array("a"=>"a"). Sẽ có ý nghĩa hơn nếu !=được thay thế bởi !==.
Pang

1
@Pang bạn đúng rồi. Tôi nghĩ rằng nhận xét của bạn chắc chắn phải sai lúc đầu, nhưng, thật ngạc nhiên, [0] == ['a']trong PHP (kể từ 0 == 'a', và, thực sự, 0 == 'banana'). ==Toán tử của PHP là điên rồ.
Đánh dấu Amery

2
Nó không hiệu quả trong chừng mực vì nó liên quan đến việc gọi Array_keys so với chỉ kiểm tra cho đến khi bạn tìm thấy một chỉ số nguyên không tuần tự. Dù sao bạn cũng đang làm điều đó , nhưng bạn đã sao chép một mảng lớn.
podperson

17

Tôi đã sử dụng cả hai array_keys($obj) !== range(0, count($obj) - 1)array_values($arr) !== $arr(là đối ngẫu của nhau, mặc dù cái thứ hai rẻ hơn cái thứ nhất) nhưng cả hai đều thất bại cho các mảng rất lớn.

Điều này là do array_keysarray_valuescả hai hoạt động rất tốn kém (vì họ xây dựng một mảng hoàn toàn mới có kích thước gần bằng với bản gốc).

Hàm sau mạnh hơn các phương thức được cung cấp ở trên:

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

Cũng lưu ý rằng nếu bạn không quan tâm đến việc phân biệt các mảng thưa thớt với các mảng kết hợp, bạn có thể chỉ cần trả về 'assoc'từ cả hai ifkhối.

Cuối cùng, mặc dù điều này có vẻ ít "thanh lịch" hơn rất nhiều "giải pháp" trên trang này, nhưng trên thực tế, nó hiệu quả hơn rất nhiều. Hầu như bất kỳ mảng kết hợp sẽ được phát hiện ngay lập tức. Chỉ các mảng được lập chỉ mục mới được kiểm tra toàn diện và các phương thức được nêu ở trên không chỉ kiểm tra các mảng được lập chỉ mục một cách triệt để, chúng sao chép chúng.


13

Tôi nghĩ rằng hai hàm sau là cách tốt nhất để kiểm tra 'nếu một mảng là kết hợp hoặc số'. Vì 'số' có thể chỉ có nghĩa là các phím số hoặc chỉ các phím số liên tiếp, hai hàm được liệt kê bên dưới để kiểm tra một trong hai điều kiện:

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

Hàm đầu tiên kiểm tra nếu mỗi khóa là một giá trị nguyên. Hàm thứ hai kiểm tra xem mỗi khóa có phải là một giá trị nguyên hay không và ngoài ra, kiểm tra xem tất cả các khóa có tuần tự bắt đầu từ $ base hay không, do đó có thể được bỏ qua nếu bạn không cần chỉ định giá trị cơ sở khác. key ($ my_array) trả về null nếu con trỏ đọc được di chuyển qua cuối mảng, đó là kết thúc vòng lặp for và làm cho câu lệnh sau vòng lặp for trở về đúng nếu tất cả các khóa là số nguyên. Nếu không, vòng lặp kết thúc sớm vì một khóa có kiểu chuỗi và câu lệnh sau vòng lặp for sẽ trả về false. Ngoài ra, hàm thứ hai thêm một đến $ cơ sở sau mỗi so sánh, để có thể kiểm tra xem khóa tiếp theo có đúng giá trị hay không. Việc so sánh nghiêm ngặt làm cho nó cũng kiểm tra xem khóa có phải là số nguyên không. Phần cơ sở $ base = (int) $ trong phần đầu tiên của vòng lặp for có thể bị bỏ qua khi $ base bị bỏ qua hoặc nếu bạn chắc chắn rằng nó chỉ được gọi bằng một số nguyên. Nhưng vì tôi không thể chắc chắn cho mọi người, nên tôi đã để nó lại. Câu lệnh chỉ được thực hiện một lần. Tôi nghĩ đây là những giải pháp hiệu quả nhất:

  • Bộ nhớ khôn ngoan: Không sao chép dữ liệu hoặc phạm vi chính. Thực hiện một mảng_values ​​hoặc Array_keys có vẻ ngắn hơn (ít mã hơn) nhưng hãy ghi nhớ những gì diễn ra trong nền sau khi bạn thực hiện cuộc gọi đó. Vâng, có nhiều câu lệnh (hiển thị) hơn trong một số giải pháp khác, nhưng đó không phải là điều đáng quan tâm, phải không?
  • Thời gian khôn ngoan: Bên cạnh thực tế là sao chép / trích xuất dữ liệu và / hoặc khóa cũng mất thời gian, giải pháp này hiệu quả hơn so với thực hiện một bài thuyết minh. Một lần nữa, một foreach có vẻ hiệu quả hơn đối với một số người vì nó ngắn hơn trong ký hiệu, nhưng trong nền foreach cũng gọi reset, key và tiếp theo là lặp. Nhưng ngoài ra, nó cũng gọi hợp lệ để kiểm tra điều kiện kết thúc, điều này được tránh ở đây do sự kết hợp với kiểm tra số nguyên.

Hãy nhớ rằng một khóa mảng chỉ có thể là một số nguyên hoặc một chuỗi và một chuỗi số nghiêm ngặt như "1" (chứ không phải "01") sẽ được dịch thành một số nguyên. Đó là những gì làm cho việc kiểm tra khóa số nguyên là thao tác cần thiết duy nhất ngoài việc đếm nếu bạn muốn mảng được tuần tự. Đương nhiên, nếu is_indexed_array trả về false, mảng có thể được xem là kết hợp. Tôi nói 'đã thấy', vì thực tế tất cả đều như vậy.


1
Đây là câu trả lời tốt nhất. Định nghĩa của mảng "kết hợp" hoặc "số" phụ thuộc vào tình huống cụ thể.
Pato

Nếu foreach kém hiệu quả hơn phương pháp được sử dụng ở đây thì ngoài sự bất tiện khi sử dụng hai chức năng khác nhau, hiệu suất của giải pháp này tốt hơn so với phương pháp của tôi (trước đó). Tôi nghi ngờ là không, vì foreach được khuyến nghị là cách nhanh nhất để đi qua một mảng.
podperson

7

Chức năng này có thể xử lý:

  • mảng có lỗ trong chỉ mục (ví dụ: 1,2,4,5,8,10)
  • mảng có các phím "0x": ví dụ: khóa '08' là liên kết trong khi khóa '8' là tuần tự.

ý tưởng rất đơn giản: nếu một trong các khóa KHÔNG phải là số nguyên, thì đó là mảng kết hợp, nếu không thì nó là tuần tự.

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

1
"Nếu một trong các khóa KHÔNG phải là số nguyên, thì đó là mảng kết hợp, nếu không thì nó là tuần tự" - hả? Không, điều này chỉ đơn giản là sai. Có chỗ để tranh luận về những gì tạo thành một mảng "kết hợp", nhưng ý nghĩa của "tuần tự" là khá rõ ràng và nó không giống như tất cả các khóa là số.
Đánh dấu Amery

Tuy nhiên, nếu một trong các khóa KHÔNG phải là số nguyên thì nó liên quan đến bản chất, tuy nhiên, nó chỉ là tuần tự nếu các khóa đi từ 0 - chiều dài (mảng) - 1. Tuy nhiên, đó là NUMERIC, nếu tất cả các khóa chỉ được đánh số, nhưng có thể hoặc có thể không hoạt động với nhiều hàm mảng yêu cầu một mảng tuần tự. Nếu bạn chuyển đổi mảng số có lỗ thành tuần tự bằng cách chạy mảng_values ​​(mảng) trên đó, thì nó sẽ được chuyển đổi thành tuần tự.
geilt

7

Tôi nhận thấy hai cách tiếp cận phổ biến cho câu hỏi này: một sử dụng array_values()và sử dụng khác key(). Để tìm ra cái nào nhanh hơn, tôi đã viết một chương trình nhỏ:

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

Đầu ra cho chương trình trên PHP 5.2 trên CentOS như sau:

Thời gian thực hiện với phương pháp # 1 = 10.745ms
Thời gian thực hiện với phương pháp # 2 = 18.239ms

Đầu ra trên PHP 5.3 mang lại kết quả tương tự. Rõ ràng array_values()là sử dụng nhanh hơn nhiều.


điểm chuẩn xấu. Bạn đã không kiểm tra các mảng lớn. Trên máy tính của tôi bắt đầu từ 10K + yếu tố phương pháp # 2 nhanh hơn. Hãy thử với$arrays = Array( 'Array #1' => range(0, 50000), );
nonsensei

7

Một cách để tiếp cận điều này là cõng trên json_encode, vốn đã có phương pháp nội bộ riêng để phân biệt giữa một mảng kết hợp và một mảng được lập chỉ mục để đưa ra JSON chính xác.

Bạn có thể làm điều này bằng cách kiểm tra xem ký tự đầu tiên được trả về sau khi mã hóa là {(mảng kết hợp) hay [(mảng được lập chỉ mục).

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

Theo tôi, ksort () là không cần thiết. Giải pháp này đang hoạt động nhưng nó phải kiểm tra xem $ Array có null không và nếu json_encode không thành công, vì vậy hãy thử / bắt. + nó không thực sự tối ưu nếu $ Array lớn.
lucbonnin

7

Đã có nhiều câu trả lời, nhưng đây là phương pháp mà Laravel dựa vào trong lớp Arr của nó:

/**
 * 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;
}

Nguồn: https://github.com/laravel/framework/blob/5.4/src/Illuminate/Support/Arr.php


1
@Casey array_keys($keys)sẽ trả về một dãy số liên tiếp (0 ... X) có cùng độ dài của mảng ban đầu. Ví dụ array_keys(["a", "b", "c"]) = [0, 1, 2]; array_keys([0, 1, 2]) = [0, 1, 2](đó là một mảng tuần tự vì [0, 1, 2] !== [0, 1, 2]). Một ví dụ khác: array_keys(["a" => 5, "b" => 7, "c" => 10]) = ["a", "b", "c"]; array_keys(["a", "b", "c"]) = [0, 1, 2](đó là một mảng kết hợp bởi vì ["a", "b", "c"] !== [0, 1, 2]). Hy vọng điều đó rõ ràng (khó giải thích rộng rãi trong một bình luận, ít nhất là đối với tôi)
valepu

Thuật toán này là điên rồ, dễ dàng, dễ hiểu.
Benyi

Điều này sẽ không hoạt động nếu bạn có một chuỗi liên tiếp của các hàng kết hợp.
lucbonnin

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

Nhanh chóng, súc tích và bộ nhớ hiệu quả. Không có so sánh đắt tiền, chức năng gọi hoặc sao chép mảng.


4

Bằng cách sử dụng phần mở rộng PHP xarray

Bạn có thể làm điều này rất nhanh (nhanh hơn khoảng 30 lần trong PHP 5.6):

if (array_is_indexed($array)) {  }

Hoặc là:

if (array_is_assoc($array)) {  }

3

Tôi biết sẽ hơi vô nghĩa khi thêm câu trả lời cho hàng đợi khổng lồ này, nhưng đây là giải pháp O (n) có thể đọc được mà không yêu cầu sao chép bất kỳ giá trị nào:

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

Thay vì kiểm tra các khóa để xem chúng có phải là số không, bạn lặp lại các phím sẽ có ở đó cho một mảng số và đảm bảo chúng tồn tại.


thêm một điểm nữa mảng ở dạng [1,2,null,4]sẽ thất bại, nhưng đó là mảng chính xác. vì vậy tôi đã thêm một số cải tiến tại stackoverflow.com/a/25206156/501831 với array_key_existskiểm tra bổ sung )
lazycommit

-1; isset()là công cụ sai ở đây vì nó sẽ trả về false nếu giá trị được đặt nhưng được null, như được chỉ ra bởi @lazycommit.
Đánh dấu Amery

3

Giải pháp của tôi:

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

array_mergetrên một mảng duy nhất sẽ reindex tất cả integercác khóa, nhưng không phải là khác. Ví dụ:

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

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

Vì vậy, nếu một danh sách (một mảng không liên kết) được tạo ['a', 'b', 'c']thì một giá trị được loại bỏ unset($a[1])sau đó array_mergeđược gọi, danh sách được giới thiệu lại bắt đầu từ 0.


-1; đây là O(n)trong bộ nhớ bổ sung được sử dụng (vì nó tạo ra nhiều mảng mới có nhiều phần tử như nhau $array), câu trả lời không giải quyết được sự mơ hồ của câu hỏi được hỏi cũng như giải thích chính xác cách nó xác định một mảng danh sách / không liên kết và thậm chí nếu cả hai điểm này đều không đúng thì điều này không rõ ràng rằng giá trị này sẽ tăng thêm bất kỳ giá trị nào so với các câu trả lời khác đã được đăng.
Đánh dấu Amery

3

Sau một số điểm chuẩn cục bộ, gỡ lỗi, thăm dò trình biên dịch, định hình và lạm dụng 3v4l.org để điểm chuẩn trên nhiều phiên bản hơn (vâng, tôi đã có cảnh báo dừng) và so sánh với mọi biến thể tôi có thể tìm thấy ...

Tôi cung cấp cho bạn một cách hữu cơ có nguồn gốc kịch bản tốt nhất-trung bình-trường hợp xấu nhất chức năng kiểm tra mảng kết hợp đó là lúc tồi tệ nhất xấp xỉ tốt như hoặc tốt hơn so với tất cả các trung bình hợp cụ thể các tình huống khác.

/**
 * 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;
}

Từ 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

Đây là phương pháp tôi sử dụng:

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')) );

Lưu ý rằng điều này không giải thích cho các trường hợp đặc biệt như:

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

unset($a[1]);

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

Xin lỗi, không thể giúp bạn với điều đó. Nó cũng phần nào biểu diễn cho các mảng có kích thước vừa phải, vì nó không tạo ra các bản sao không cần thiết. Chính những điều nhỏ nhặt này đã khiến Python và Ruby trở nên đẹp hơn rất nhiều để viết trong ...: 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);
}

?>

Cả hai ví dụ này, ghi được nhiều điểm nhất đều không hoạt động chính xác với các mảng như $array = array('foo' => 'bar', 1)


+1 is_list () của bạn là IMO câu trả lời hay nhất. Một số người không có đầu mối về độ phức tạp của không gian và thời gian và chức năng kịch bản gốc so với PHP ...
e2-e4

2

Điều này cũng sẽ làm việc ( bản demo ):

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

Xin lưu ý rằng điểm chính của câu trả lời này là thông báo cho bạn về sự tồn tại SplFixedArrayvà không khuyến khích bạn sử dụng Ngoại lệ cho các loại thử nghiệm này.


2

Tôi nghĩ định nghĩa của một mảng vô hướng sẽ thay đổi theo ứng dụng. Đó là, một số ứng dụng sẽ đòi hỏi một ý thức nghiêm ngặt hơn về những gì đủ điều kiện là một mảng vô hướng, và một số ứng dụng sẽ đòi hỏi một cảm giác lỏng lẻo hơn.

Dưới đây tôi trình bày 3 phương pháp khác nhau nghiêm ngặt.

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

Thêm một nguồn nhanh nữa . Mã hóa phù hợp của json_encode(và bson_encode). Vì vậy, có javascript Array tuân thủ.

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
Tại sao issetarray_key_exists? cái sau sẽ không đủ?
mcfedr

@mcfedr vâng, nó sẽ - isset()kiểm tra ở đây là hoàn toàn dư thừa.
Đánh dấu Amery

@mcfedr, @ mark-amery vì lý do hiệu suất. isset()nhanh hơn array_key_exists(). xem ilia.ws/archives/ từ
lazycommit

@lazycommit Nó sẽ phụ thuộc vào mảng của bạn sau đó phụ thuộc vào việc nó có tốt hơn hay không, không có khả năng có một mảng có nhiều nulls, nhưng cũng không chắc là bạn có mảng đủ lớn để có sự khác biệt hiệu năng đáng chú ý bằng cách sử dụng cả hai séc
mcfedr

2
nếu bạn cần kiểm tra xem nó có phù hợp hay không json_encode, bạn chỉ cần kiểm tra biểu tượng đầu tiên của chuỗi, được trả về bởi json_encode($your_arr)- cho dù đó là [hay {;-)
pilat

2

Đây có thể là giải pháp?

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

Thông báo trước rõ ràng là con trỏ mảng được đặt lại nhưng tôi có thể nói rằng chức năng được sử dụng trước khi mảng thậm chí được dịch chuyển hoặc sử dụng.


Hàm này trả về false cho cả hai array("a", "b")array("a", "b" => "B")vì nó chỉ kiểm tra khóa đầu tiên. BTW, is_longchỉ là một bí danh củais_int .
Pang

1
khá thẳng thắn tôi nghĩ rằng điều này sẽ rất hiệu quả trong phần lớn các trường hợp, và hiệu quả hơn nhiều so với các giải pháp thay thế. Nếu bạn hiểu hậu quả của phương pháp này và nhận ra rằng nó sẽ hiệu quả với bạn, đó có thể là sự lựa chọn tốt nhất.
Gershom

Điều này đơn giản là sai; nó chỉ nhìn vào chìa khóa đầu tiên
Đánh dấu Amery

@MarkAmery câu hỏi hỏi làm thế nào để phân biệt các mảng tuần tự thuần túy với các mảng hoàn toàn liên kết. Câu trả lời này thực hiện chính xác điều đó và là hiệu quả nhất của tất cả chúng. Có hành vi không xác định cho mảng hỗn hợp là hoàn toàn tốt trong bối cảnh của câu hỏi. +1
Tobia

@Tobia Tôi không nghĩ rằng hầu hết mọi người sẽ đồng ý với bạn phân loại, giả sử, [7 => 'foo', 2 => 'bar']như một mảng "hỗn hợp", một phần nhưng không hoàn toàn "tuần tự". Điều đó có vẻ như là một cách sử dụng từ ngữ không chính xác với tôi.
Đánh dấu Amery

2

Rất nhiều giải pháp ở đây thanh lịch và đẹp mắt, nhưng không có quy mô tốt và tốn nhiều bộ nhớ hoặc CPU. Hầu hết đang tạo ra 2 điểm dữ liệu mới trong bộ nhớ với giải pháp này từ cả hai phía so sánh. Mảng càng lớn thì quá trình và bộ nhớ được sử dụng càng khó và càng lâu, và bạn mất đi lợi ích của việc đánh giá ngắn mạch. Tôi đã làm một số thử nghiệm với một vài ý tưởng khác nhau. Cố gắng tránh mảng_key_exists vì nó tốn kém và cũng tránh tạo ra các bộ dữ liệu lớn mới để so sánh. Tôi cảm thấy đây là một cách đơn giản để biết nếu một mảng là tuần tự.

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

Bạn chạy một số đếm duy nhất trên mảng chính và lưu trữ một số nguyên duy nhất. Sau đó, bạn lặp qua mảng và kiểm tra sự trùng khớp chính xác trong khi lặp lại bộ đếm. Bạn nên có từ 1 đến đếm. Nếu nó không thành công, nó sẽ bị đoản mạch giúp tăng hiệu suất khi nó sai.

Ban đầu tôi đã làm điều này với một vòng lặp for và kiểm tra ngay lập tức ($ Array [$ i]) nhưng điều này sẽ không phát hiện ra các khóa null yêu cầu mảng_key_exists và như chúng ta biết đó là hàm tồi nhất để sử dụng cho tốc độ.

Cập nhật liên tục các biến qua foreach để kiểm tra cùng với trình vòng lặp không bao giờ vượt quá kích thước nguyên của nó, hãy sử dụng PHP để tích hợp tối ưu hóa bộ nhớ, bộ nhớ đệm và bộ sưu tập rác để giữ cho bạn sử dụng tài nguyên rất thấp.

Ngoài ra, tôi sẽ lập luận rằng việc sử dụng Array_keys trong foreach là ngớ ngẩn khi bạn chỉ cần chạy $ key => $ value và kiểm tra khóa. Tại sao tạo điểm dữ liệu mới? Khi bạn trừu tượng hóa các phím mảng, bạn đã tiêu thụ nhiều bộ nhớ hơn ngay lập tức.


1

câu trả lời đã được đưa ra nhưng có quá nhiều thông tin sai lệch về hiệu suất. Tôi đã viết kịch bản điểm chuẩn nhỏ này cho thấy phương pháp foreach là nhanh nhất.

Tuyên bố từ chối trách nhiệm: các phương pháp sau đây đã được sao chép từ các câu trả lời khác

<?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";
}

các kết quả:

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

Hoặc bạn chỉ có thể sử dụng này:

Arr::isAssoc($array)

sẽ kiểm tra xem mảng có chứa bất kỳ khóa không số nào hay không:

Arr:isAssoc($array, true)

để kiểm tra xem mảng có đúng thứ tự hay không (chứa các khóa int được tạo tự động 0 đến n-1 )

sử dụng thư viện này


0

Trừ khi PHP có tích hợp sẵn cho điều đó, bạn sẽ không thể làm điều đó trong ít hơn O (n) - liệt kê tất cả các khóa và kiểm tra loại số nguyên. Trên thực tế, bạn cũng muốn đảm bảo không có lỗ hổng, vì vậy thuật toán của bạn có thể trông như sau:

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

Nhưng tại sao phải bận tâm? Chỉ cần giả sử mảng là loại bạn mong đợi. Nếu không, nó sẽ nổ tung trên mặt bạn - đó là chương trình năng động dành cho bạn! Kiểm tra mã của bạn và tất cả sẽ tốt ...


1
Thông thường chỉ cần giả sử mảng là loại mong muốn sẽ là cách để đi. Nhưng trong trường hợp của tôi, tôi đang lặp qua một mảng nhiều chiều và định dạng đầu ra tùy thuộc vào loại mảng nào mà một nút đã cho.
Wilco

0

Tôi so sánh sự khác biệt giữa các khóa của mảng và các khóa kết quả của mảng_values ​​() của mảng, sẽ luôn là một mảng với các chỉ số nguyên. Nếu các khóa là như nhau, nó không phải là một mảng kết hợp.

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

-1; cái này dùngO(n) bộ nhớ bổ sung khi $arraynvật phẩm, và viết (someboolean) ? false : truethay vì !somebooleanlà dài dòng khủng khiếp và vô cớ.
Đánh dấu Amery
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.