จะตรวจสอบว่าอาร์เรย์ของ PHP เชื่อมโยงหรือเรียงตามลำดับได้อย่างไร?


781

PHP ถือว่าอาร์เรย์ทั้งหมดเป็นแบบเชื่อมโยงดังนั้นจึงไม่มีฟังก์ชั่นในตัว ทุกคนสามารถแนะนำวิธีที่มีประสิทธิภาพพอสมควรในการตรวจสอบว่าอาร์เรย์มีคีย์ตัวเลขหรือไม่?

โดยทั่วไปฉันต้องการแยกความแตกต่างระหว่างสิ่งนี้:

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

และนี่:

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

382
มีข้อบกพร่องในรหัสของคุณ: มะเขือเทศเป็นผลไม้
Olle Härstedt

9
วิธีนี้มีข้อแม้ แต่บ่อยครั้งที่ฉันเพิ่งทำif (isset($array[0]))ซึ่งง่ายและรวดเร็ว แน่นอนคุณควรแน่ใจก่อนว่าอาเรย์ไม่ว่างเปล่าและคุณควรมีความรู้เกี่ยวกับเนื้อหาที่เป็นไปได้ของอาเรย์เพื่อให้วิธีการไม่สามารถล้มเหลวได้ (เช่นตัวเลข / การเชื่อมโยงแบบผสมหรือไม่เรียงตามลำดับ)
Gras Double

@ OlleHärstedtไม่ได้ตามที่ศาลสหรัฐสูง ;-)
MC Emperor

คำตอบ:


622

คุณได้ถามคำถามสองข้อที่ไม่เท่ากัน:

  • ประการแรกวิธีการตรวจสอบว่าอาร์เรย์มีเพียงปุ่มตัวเลขเท่านั้น
  • ประการที่สองวิธีการตรวจสอบว่าอาร์เรย์มีคีย์ตัวเลขตามลำดับเริ่มต้นจาก 0

พิจารณาว่าคุณต้องการพฤติกรรมแบบใด (อาจเป็นได้ทั้งที่จะทำเพื่อวัตถุประสงค์ของคุณ)

คำถามแรก (เพียงตรวจสอบว่าคีย์ทั้งหมดเป็นตัวเลข) จะตอบได้ดีโดยกัปตัน Kuro

สำหรับคำถามที่สอง (ตรวจสอบว่าอาร์เรย์นั้นมีดัชนีเป็นศูนย์และเรียงตามลำดับ) หรือไม่คุณสามารถใช้ฟังก์ชันต่อไปนี้:

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

30
ฉันคิดว่ามันมีประโยชน์มากกว่าที่จะคิดว่าอาร์เรย์ลำดับเป็นกรณีพิเศษของการเชื่อมโยงอาร์เรย์ ดังนั้นทุกอาเรย์เป็นแบบเชื่อมโยง แต่มีบางลำดับเท่านั้น ดังนั้นการทำงานที่จะทำให้ความรู้สึกมากกว่าisSequential() isAssoc()ในฟังก์ชั่นเช่นนี้อาร์เรย์ที่ว่างควรถูกมองว่าเป็นแบบลำดับ array() === $arr || !isAssoc($arr)สูตรอาจจะ
donquixote

18
ฉันคิดว่านี่จะหลีกเลี่ยงเวลา cpu และหน่วยความจำที่เป็นไปได้มากมายหากใครจะตรวจสอบว่า isset ($ arr [0]) เป็นเท็จก่อนที่จะแยกคีย์ทั้งหมดตามที่มันเชื่อมโยงกันอย่างชัดเจนถ้าอาร์เรย์ไม่ว่างเปล่า แต่ไม่มีองค์ประกอบใน 0 ตำแหน่ง. ในฐานะที่เป็นอาร์เรย์เชื่อมโยงที่แท้จริง "ส่วนใหญ่" มีสตริงเป็นคีย์ซึ่งควรเป็นการเพิ่มประสิทธิภาพที่ดีสำหรับกรณีทั่วไปของฟังก์ชันดังกล่าว
OderWat

10
@OderWat - การเพิ่มประสิทธิภาพของคุณควรใช้array_key_existsแทนissetเพราะถ้าองค์ประกอบศูนย์เป็นค่า Null, ผู้ออกจะกลับเท็จอย่างไม่ถูกต้อง โดยทั่วไปแล้วค่า Null ควรเป็นค่าที่ถูกต้องในอาเรย์นั้น
OCDev

@Machitgarha การแก้ไขของคุณเปลี่ยนพฤติกรรมของฟังก์ชั่นโดยไม่มีคำอธิบายว่าทำไมและทำให้มันขัดแย้งกับคำอธิบายในร้อยแก้วเหนือสิ่งที่ควรทำ ฉันเปลี่ยนกลับแล้ว
Mark Amery

431

ในการตรวจสอบเพียงว่าอาเรย์นั้นมีคีย์ที่ไม่ใช่จำนวนเต็มหรือไม่ (ไม่ว่าอาเรย์นั้นจะเรียงตามลำดับดัชนีหรือเป็นศูนย์หรือไม่ก็ตาม)

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

หากมีอย่างน้อยหนึ่งคีย์สตริง$arrayจะถือว่าเป็นอาร์เรย์ที่เชื่อมโยง


22
วิธีนี้ดีกว่าที่คิด ถ้า count (filtered_array) == count (original_array) แสดงว่าเป็นอาร์เรย์ assoc ถ้า count (filtered_array) == 0 แสดงว่าเป็นอาร์เรย์ที่ทำดัชนี หาก count (filtered_array) <count (original_array) ดังนั้นอาร์เรย์จะมีทั้งตัวเลขและสตริงคีย์
Jamol

5
@ MikePretzlaw แน่นอน iterates; มี (ชัด) ไม่มีวิธีที่เป็นไปได้เพื่อตรวจสอบว่าคีย์ทั้งหมดของอาร์เรย์เป็น ints โดยไม่ต้องดูคีย์ทั้งหมดในอาร์เรย์ ฉันถือว่าทางเลือกที่ไม่ใช่การวนซ้ำที่เราควรจะเห็นด้านล่างเป็นตัวเลือก$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% ของอาร์เรย์ มันจะมีประสิทธิภาพมากขึ้นโดยเฉพาะถ้าคุณกำลังจัดการกับอาร์เรย์ขนาดใหญ่ถ้าคุณกำลังตรวจสอบสตริงหรือ int และโพล่งออกมาในครั้งแรกที่คุณพบ ตัวอย่างเช่น: function isAssociative($arr) { foreach ($arr as $key => $value) { if (is_string($key)) return true; } return false; }
คิดว่า

1
@Thought รหัสของคุณทำงานเร็วมาก แต่ก็ไม่สามารถตรวจจับarray ตามลำดับได้ ตัวอย่างarray(1 => 'a', 0 => 'b', 2 => 'c')จะกลายเป็นfalse(อาร์เรย์ลำดับ) ในขณะที่มันควรจะเป็นtrue(อาร์เรย์ที่เชื่อมโยง) toolsqa.com/data-structures/array-in-programming ฉันไม่แน่ใจว่ากุญแจจะต้องเรียงลำดับจากน้อยไปมากหรือไม่ (0, 1, ... )
วี

132

แน่นอนว่านี่เป็นทางเลือกที่ดีกว่า

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

52
สิ่งนี้จะทำซ้ำค่าในอาเรย์ซึ่งอาจมีราคาแพงมาก คุณเก่งกว่าการตรวจสอบคีย์อาเรย์
meagar

8
ฉันเพิ่งใช้ ==; ฉันไม่คิดว่าจำเป็นต้อง === ที่นี่ แต่การตอบ "ไม่ได้ตั้งค่าและใช้งานไม่ได้": เมื่อคุณยกเลิกการตั้งค่าองค์ประกอบแรกมันจะไม่อาร์เรย์ที่เป็นดัชนีจำนวนเต็มเริ่มต้นที่ 0 ดังนั้น IMO จึงใช้งานได้
grantwparks

4
เห็นด้วยกับ @grantwparks: อาร์เรย์ที่กระจัดกระจายไม่ได้จัดทำดัชนี ที่น่าสนใจเพราะไม่มีทางที่จะลบองค์ประกอบออกจากส่วนกลางของอาร์เรย์ที่ทำดัชนี PHP โดยทั่วไปจะประกาศอาร์เรย์ทั้งหมดเนื่องจากการเชื่อมโยงและตัวเลขเป็นเพียง 'การสร้างคีย์สำหรับฉัน'
RickMeasham

7
ปัญหาเดียวที่ฉันมีคือมัน===จะเสียเวลาในการตรวจสอบว่าค่าเท่ากันแม้ว่าเราจะสนใจเฉพาะกุญแจเท่านั้น ด้วยเหตุนี้ฉันชอบ$k = array_keys( $arr ); return $k === array_keys( $k );รุ่น
Jesse

5
หมายเหตุเพิ่มเติมสิ่งนี้จะล้มเหลวในอาร์เรย์ที่ระบุด้วยปุ่มตัวเลขที่ไม่เป็นไปตามลำดับ เช่น $ myArr = array (0 => 'a', 3 => 'b', 4 => 1, 2 => 2, 1 => '3'); สิ่งหนึ่งที่อาจเป็นไปได้คือการเรียกใช้ 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);

สิ่งนี้ได้ผลเพราะ key () คืนค่า NULL เมื่อตำแหน่งอาเรย์ปัจจุบันไม่ถูกต้องและ 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

2
การใช้foreachแทนการทำซ้ำอย่างชัดเจนนั้นเร็วกว่าประมาณสองเท่า
ofavre

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: ปุ่มทั้งหมดเป็นตัวเลข / จำนวนเต็ม

หมายเหตุ : ฟังก์ชั่นนี้จะคืนค่าจริงสำหรับอาร์เรย์ว่างเช่นกัน

//! 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: ปุ่มทั้งหมดเป็นสตริง

หมายเหตุ : ฟังก์ชั่นนี้จะคืนค่าจริงสำหรับอาร์เรย์ว่างเช่นกัน

//! 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. ปุ่มบางอย่างจะสาย , กุญแจบางคนที่เป็นตัวเลข / จำนวนเต็ม

หมายเหตุ : ฟังก์ชั่นนี้จะคืนค่าจริงสำหรับอาร์เรย์ว่างเช่นกัน

//! 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 คีย์จะเป็นตัวเลข / จำนวนเต็ม , ลำดับและzero-based

หมายเหตุ : ฟังก์ชั่นนี้จะคืนค่าจริงสำหรับอาร์เรย์ว่างเช่นกัน

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

Caveats / Pitfalls (หรือข้อเท็จจริงที่แปลกประหลาดยิ่งขึ้นเกี่ยวกับคีย์อาร์เรย์ใน 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" จะไม่ถูกโยนเนื่องจากไม่ใช่จำนวนเต็มทศนิยมที่ถูกต้อง

ตัวอย่างเช่นคีย์สำหรับอาร์เรย์เหล่านี้คือจำนวนเต็ม :

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.

มีอะไรเพิ่มเติมตามdoc ,

ขนาดของจำนวนเต็มขึ้นอยู่กับแพลตฟอร์มแม้ว่าค่าสูงสุดประมาณสองพันล้านเป็นค่าปกติ (ที่ลงนาม 32 บิต) โดยทั่วไปแพลตฟอร์ม 64 บิตจะมีค่าสูงสุดประมาณ 9E18 ยกเว้นสำหรับ Windows ซึ่งเป็น 32 บิตเสมอ PHP ไม่รองรับจำนวนเต็มที่ไม่ได้ลงนาม

ดังนั้นคีย์สำหรับอาเรย์นี้อาจเป็นหรือไม่เป็นจำนวนเต็มก็ได้ขึ้นอยู่กับแพลตฟอร์มของคุณ

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

แม้เลว, PHP มีแนวโน้มที่จะรถถ้าจำนวนเต็มอยู่ใกล้ 2 31 = 2,147,483,648 เขตแดน (ดูข้อผิดพลาด 51430 , ข้อผิดพลาด 52899 ) ตัวอย่างเช่นในสภาพแวดล้อมท้องถิ่นของฉัน (PHP 5.3.8 บน XAMPP 1.7.7 บน Windows 7) var_dump(array("2147483647" => "b"))ให้

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

แต่ในการสาธิตสดนี้บนแผ่นจดบันทึก (PHP 5.2.5) การแสดงออกเดียวกันให้

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

ดังนั้นที่สำคัญเป็นจำนวนเต็มในสภาพแวดล้อมหนึ่ง แต่สตริงอีกแม้ว่าจะ2147483647เป็นที่ถูกต้องลงนาม 32 บิตจำนวนเต็ม


2
ยกเว้นตามที่ฉันพูดถึงด้านล่างมันเกี่ยวข้องกับการสร้างอาร์เรย์ที่ซ้ำกันกับอาร์เรย์ที่ถูกตรวจสอบทำให้มีราคาแพงมากสำหรับอาร์เรย์ขนาดใหญ่
podperson

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')จะถือเป็นอาเรย์แบบเชื่อมโยงในขณะที่มันไม่ใช่ (คีย์เป็นสตริงจริง ๆ )
Captain 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')ไม่ได้เป็น
ปาง

@CaptainkurO คุณไม่ถูกต้อง '1' และ '2' จะถูกจัดเก็บเป็นจำนวนเต็ม อ่านส่วนที่ยกมาของคำตอบของกระรอกจาก 11 พฤษภาคม 2011 เวลา 19:34 PHP ไม่ได้จัดเก็บคีย์สตริงที่ดูเหมือนจำนวนเต็ม มันแปลงเหล่านั้นเป็นจำนวนเต็ม
Buttle Butkus

20

จริงๆแล้ววิธีที่มีประสิทธิภาพที่สุดคือ:

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

งานนี้เพราะจะเปรียบเทียบคีย์ (ซึ่งสำหรับอาร์เรย์ลำดับอยู่เสมอ 0,1,2 ฯลฯ ) กับคีย์ของคีย์ (ซึ่งจะเสมอเป็น 0,1,2 ฯลฯ )


1
ฉลาด แต่ไม่ดี ทำไม "มีประสิทธิภาพมากที่สุด" นี้ มันจะสามารถอ่านได้มากขึ้นเพื่อเปรียบเทียบ array_keys ($ a) กับช่วง (0, count ($ a)) ทางออกที่ฉลาดที่สุดไม่ค่อยเป็นสิ่งที่ดีที่สุดในประสบการณ์ของฉัน โดยเฉพาะอย่างยิ่งเมื่อความฉลาดนั้นไม่เพิ่มคุณค่าใด ๆ ให้กับทางเลือกที่ชัดเจนและสะอาด
เชน H

4
ฟังก์ชั่นนี้ผลตอบแทนtrueสำหรับarray(1=>"a")แต่สำหรับfalse array("a"=>"a")จะมีความหมายมากขึ้นถ้าจะถูกแทนที่ด้วย!= !==
ปาง

1
@Pang คุณถูกต้อง ฉันคิดว่าความคิดเห็นของคุณจะต้องผิดอย่างแน่นอนในตอนแรก แต่ความประหลาดใจของฉัน[0] == ['a']ใน PHP (ตั้งแต่0 == 'a'และแน่นอน0 == 'banana') ==ผู้ประกอบการของ PHP เสียสติ
Mark Amery

2
มันไม่ได้มีประสิทธิภาพตราบเท่าที่มันเกี่ยวข้องกับการเรียก array_keys และเพียงแค่ตรวจสอบจนกว่าคุณจะพบดัชนีจำนวนเต็มแบบไม่ต่อเนื่อง ภายใต้ประทุนที่คุณกำลังทำแบบนั้นอยู่แล้วแต่คุณซ้ำแล้วอาร์เรย์ใหญ่
podperson

17

ฉันเคยใช้ทั้งสองarray_keys($obj) !== range(0, count($obj) - 1)และarray_values($arr) !== $arr(ซึ่งเป็น duals ของกันและกันแม้ว่าที่สองมีราคาถูกกว่าครั้งแรก) แต่ทั้งสองล้มเหลวสำหรับอาร์เรย์ขนาดใหญ่มาก

นี้เป็นเพราะ 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

ฉันคิดว่าทั้งสองฟังก์ชั่นต่อไปนี้เป็นวิธีที่ดีที่สุดในการตรวจสอบว่า 'อาเรย์เป็นแบบเชื่อมโยงหรือตัวเลข' เนื่องจาก 'ตัวเลข' อาจหมายถึงเฉพาะคีย์ตัวเลขหรือคีย์ตัวเลขแบบลำดับเท่านั้นฟังก์ชันทั้งสองจึงอยู่ในรายการด้านล่างซึ่งตรวจสอบเงื่อนไขอย่างใดอย่างหนึ่ง:

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 และสามารถละเว้นได้หากคุณไม่จำเป็นต้องระบุค่าฐานอื่น key ($ my_array) ส่งคืนค่า null หากตัวชี้การอ่านถูกย้ายผ่านจุดสิ้นสุดของอาร์เรย์ซึ่งเป็นสิ่งที่สิ้นสุดสำหรับลูปและทำให้คำสั่งหลังจาก for loop ส่งคืนจริงถ้าคีย์ทั้งหมดเป็นจำนวนเต็ม มิฉะนั้นลูปจะสิ้นสุดก่อนกำหนดเนื่องจากคีย์เป็นสตริงชนิดและคำสั่งหลังจาก for for จะส่งคืนค่าเท็จ ฟังก์ชันหลังนอกเหนือจากเพิ่มหนึ่งถึง $ base หลังจากการเปรียบเทียบแต่ละครั้งเพื่อให้สามารถตรวจสอบว่าคีย์ถัดไปเป็นค่าที่ถูกต้องหรือไม่ การเปรียบเทียบแบบเข้มงวดทำให้ตรวจสอบว่าคีย์เป็นจำนวนเต็มชนิดหรือไม่ $ base = (int) $ base ส่วนในส่วนแรกของ for loop สามารถปล่อยออกมาได้เมื่อ $ base ถูกละไว้หรือถ้าคุณแน่ใจว่ามันถูกเรียกโดยใช้จำนวนเต็มเท่านั้น แต่เนื่องจากฉันไม่สามารถมั่นใจได้สำหรับทุกคนฉันจึงทิ้งไว้คำสั่งจะถูกดำเนินการเพียงครั้งเดียวเท่านั้น ฉันคิดว่านี่เป็นโซลูชั่นที่มีประสิทธิภาพที่สุด:

  • หน่วยความจำฉลาด: ไม่มีการคัดลอกข้อมูลหรือช่วงคีย์ การทำ array_values ​​หรือ array_keys อาจดูเหมือนสั้นกว่า (รหัสน้อยกว่า) แต่โปรดจำไว้ว่าเกิดอะไรขึ้นในพื้นหลังเมื่อคุณโทรออก ใช่มีคำสั่ง (มองเห็น) มากกว่าในโซลูชันอื่น ๆ แต่นั่นไม่ใช่สิ่งที่นับ
  • เวลาที่ชาญฉลาด: นอกจากการคัดลอก / แยกข้อมูลและ / หรือคีย์ยังใช้เวลาโซลูชันนี้มีประสิทธิภาพมากกว่าการทำ foreach อีกครั้ง foreach อาจดูมีประสิทธิภาพมากขึ้นสำหรับบางคนเพราะมันสั้นกว่าในโน้ต แต่ในพื้นหลัง foreach ยังเรียกการตั้งค่าใหม่ที่สำคัญและถัดไปจะทำมันวน แต่นอกจากนี้มันยังเรียกว่าถูกต้องเพื่อตรวจสอบสภาพสิ้นสุดซึ่งหลีกเลี่ยงที่นี่เนื่องจากการรวมกันกับการตรวจสอบจำนวนเต็ม

โปรดจำไว้ว่าคีย์อาเรย์จะต้องเป็นจำนวนเต็มหรือสตริงเท่านั้นและสตริงตัวเลขอย่างเคร่งครัดเช่น "1" (แต่ไม่ใช่ "01") จะถูกแปลเป็นจำนวนเต็ม ซึ่งเป็นสิ่งที่ทำให้การตรวจสอบคีย์จำนวนเต็มเป็นการดำเนินการที่จำเป็นเพียงอย่างเดียวนอกเหนือจากการนับถ้าคุณต้องการให้อาร์เรย์เรียงตามลำดับ โดยธรรมชาติถ้า is_indexed_array ส่งคืนค่าเท็จอาร์เรย์สามารถถูกเชื่อมโยงเป็น ฉันพูดว่า 'เห็น' เพราะจริงๆแล้วพวกเขาทั้งหมดเป็น


1
นี่คือคำตอบที่ดีที่สุด คำจำกัดความของอาร์เรย์ "เชื่อมโยง" หรือ "ตัวเลข" ขึ้นอยู่กับสถานการณ์ที่เฉพาะเจาะจง
Pato

หาก foreach นั้นมีประสิทธิภาพน้อยกว่าวิธีที่ใช้ในที่นี้นอกเหนือจากความไม่สะดวกในการใช้สองฟังก์ชั่นที่ต่างกันประสิทธิภาพของโซลูชันนี้จะดีกว่าของฉัน (ก่อนหน้านี้) ฉันสงสัยว่ามันไม่ได้เนื่องจากมีการแนะนำ foreach เป็นวิธีที่เร็วที่สุดในการผ่านอาร์เรย์
podperson

7

ฟังก์ชั่นนี้สามารถจัดการ:

  • อาร์เรย์ที่มีรูในดัชนี (เช่น 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;
}

1
"ถ้าปุ่มใดปุ่มหนึ่งไม่ใช่จำนวนเต็มจะเป็นอาเรย์แบบเชื่อมโยงมิฉะนั้นจะเรียงตามลำดับ" - ฮะเหรอ? ไม่ผิดนี่แค่ผิดปกติ มีที่ว่างสำหรับการถกเถียงกันว่าอะไรคือสิ่งที่ประกอบไปด้วยอาร์เรย์ "เชื่อมโยง" แต่ความหมายของ "ลำดับ" นั้นค่อนข้างชัดเจนและไม่เหมือนกับปุ่มทั้งหมดที่เป็นตัวเลข
Mark Amery

หากหนึ่งในคีย์นั้นไม่ได้เป็นจำนวนเต็มมันจะเชื่อมโยงกันโดยธรรมชาติอย่างไรก็ตามมันจะเรียงตามลำดับเฉพาะหากปุ่มเลื่อนจาก 0 - ความยาว (อาเรย์) - 1 มันคือ NUMERIC อย่างไรก็ตามหากคีย์ทั้งหมดเป็นตัวเลขเท่านั้น แต่อาจ หรืออาจใช้ไม่ได้กับฟังก์ชั่นอาเรย์หลายตัวที่ต้องใช้อาเรย์แบบลำดับ หากคุณแปลงอาเรย์ตัวเลขด้วยรูเป็นลำดับโดยการเรียกใช้ array_values ​​(อาเรย์) ในนั้นจะถูกแปลงเป็นลำดับ
geilt

7

ผมสังเกตเห็นสองวิธีที่นิยมสำหรับคำถามนี้: หนึ่งใช้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";

ผลลัพธ์สำหรับโปรแกรมบน PHP 5.2 บน CentOS มีดังนี้:

เวลาที่ถ่ายด้วยวิธี # 1 = 10.745ms
เวลาที่ถ่ายด้วยวิธี # 2 = 18.239ms

ผลลัพธ์บน PHP 5.3 ให้ผลลัพธ์ที่คล้ายกัน เห็นได้ชัดว่าการใช้array_values()เร็วกว่ามาก


มาตรฐานที่ไม่ดี คุณไม่ได้ทดสอบอาร์เรย์ขนาดใหญ่ ในคอมพิวเตอร์ของฉันเริ่มต้นที่ 10K + องค์ประกอบวิธี # 2 เร็วขึ้น ลองกับ$arrays = Array( 'Array #1' => range(0, 50000), );
nonsensei

7

วิธีหนึ่งในการเข้าถึงสิ่งนี้คือการ piggyback บนjson_encodeซึ่งมีวิธีการภายในที่แตกต่างกันระหว่างอาเรย์แบบเชื่อมโยงกับอาเรย์แบบดัชนีเพื่อส่งออก JSON ที่ถูกต้อง

คุณสามารถทำได้โดยตรวจสอบเพื่อดูว่าอักขระตัวแรกที่ส่งคืนหลังจากการเข้ารหัสคือ{(อาเรย์แบบเชื่อมโยง) หรือ[(อาเรย์แบบดัชนี)

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

ksort () ไม่จำเป็นในความคิดของฉัน วิธีการแก้ปัญหานี้ใช้งานได้ แต่ต้องทดสอบว่า $ arr เป็นโมฆะหรือไม่และถ้า json_encode ล้มเหลวลอง / ลองดู + มันไม่ดีที่สุดถ้า $ 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

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

รวดเร็วกระชับและหน่วยความจำมีประสิทธิภาพ ไม่มีการเปรียบเทียบราคาแพงการเรียกใช้ฟังก์ชันหรือการคัดลอกอาเรย์


4

โดยใช้ส่วนขยาย PHP xarray

คุณสามารถทำได้เร็วมาก (เร็วกว่าประมาณ 30+ ใน PHP 5.6):

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.com/a/25206156/501831พร้อมการarray_key_existsตรวจสอบเพิ่มเติม)
lazycommit

-1; isset()เป็นเครื่องมือที่ไม่ถูกต้องที่นี่เพราะมันจะกลับเท็จถ้าตั้งค่า แต่เป็นnullตามที่ชี้ให้เห็นโดย @lazycommit
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 คำตอบที่ดีที่สุด บางคนไม่ได้เบาะแสเกี่ยวกับเวลาและความซับซ้อนของพื้นที่และพื้นเมือง VS ฟังก์ชัน 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

หนึ่งได้อย่างรวดเร็วมากขึ้นจากแหล่งที่มา พอดีกับการเข้ารหัสของjson_encode(และbson_encode) ดังนั้นจึงมีความสอดคล้องกับจาวาสคริปต์

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

@mcfedr ใช่มันจะ - isset()ตรวจสอบที่นี่ซ้ำซ้อนอย่างสมบูรณ์
Mark Amery

@mcfedr, @ mark-amery เนื่องจากเหตุผลด้านประสิทธิภาพ จะเร็วกว่าisset() array_key_exists()ดูilia.ws/archives/…
lazycommit

@lazycommit มันขึ้นอยู่กับอาเรย์ของคุณแล้วว่ามันจะดีกว่ามีหรือไม่มีไม่ใช่ว่ามันจะมีอาเรย์ที่มีจำนวนมากnullแต่ก็ไม่ใช่ว่าคุณจะมีอาเรย์ที่ใหญ่พอที่จะเห็นความแตกต่างของประสิทธิภาพ โดยใช้การตรวจสอบทั้งสอง
mcfedr

2
ถ้าคุณต้องการตรวจสอบว่ามันจะพอดีjson_encodeคุณสามารถตรวจสอบสัญลักษณ์แรกของสตริงที่ส่งกลับโดยjson_encode($your_arr)- ไม่ว่าจะเป็น[หรือ{;-) #
23717

2

นี่อาจเป็นทางออกหรือไม่

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

ข้อแม้เห็นได้ชัดว่าเคอร์เซอร์อาร์เรย์ถูกรีเซ็ต แต่ฉันอาจจะใช้ฟังก์ชั่นก่อนที่อาร์เรย์จะถูกสำรวจหรือใช้งาน


ฟังก์ชั่นนี้จะส่งกลับเท็จสำหรับทั้งสองarray("a", "b")และarray("a", "b" => "B")มันจะตรวจสอบคีย์แรกเท่านั้น BTW, เป็นเพียงนามแฝงของis_long is_int
ปาง

1
ค่อนข้างตรงไปตรงมาฉันคิดว่านี่จะมีประสิทธิภาพมากในกรณีส่วนใหญ่และมีประสิทธิภาพมากกว่าทางเลือก หากคุณเข้าใจผลที่ตามมาของวิธีการนี้และตระหนักว่าวิธีนี้ใช้ได้ผลสำหรับคุณก็น่าจะเป็นทางเลือกที่ดีที่สุด
Gershom

นี่เป็นสิ่งที่ผิด มันจะดูที่คีย์แรกเท่านั้น
Mark Amery

@ MarkAmery คำถามที่ถามถึงวิธีการแยกความแตกต่างอย่างหมดจดอาร์เรย์ลำดับจากอาร์เรย์เชื่อมโยงอย่างหมดจด คำตอบนี้ทำอย่างนั้นและมีประสิทธิภาพที่สุดของพวกเขาทั้งหมด การมีพฤติกรรมที่ไม่ได้กำหนดสำหรับอาร์เรย์แบบผสมนั้นเป็นเรื่องปกติในบริบทของคำถาม +1
เบีย

@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 ถึงนับ ถ้ามันล้มเหลวมันจะลัดวงจรซึ่งจะช่วยเพิ่มประสิทธิภาพให้คุณเมื่อมันเป็นเท็จ

เดิมทีฉันทำสิ่งนี้พร้อมกับ for loop และตรวจสอบ isset ($ arr [$ i]) แต่สิ่งนี้จะไม่ตรวจพบคีย์ null ซึ่งต้องการ array_key_exists

อัปเดตตัวแปรอย่างต่อเนื่องผ่าน foreach เพื่อตรวจสอบพร้อมกับตัววนซ้ำที่ไม่เคยเติบโตขนาดของจำนวนเต็มมาใช้ PHP ในการเพิ่มประสิทธิภาพหน่วยความจำแคชและการรวบรวมขยะเพื่อให้คุณใช้ทรัพยากรน้อยมาก

นอกจากนี้ฉันจะยืนยันว่าการใช้ array_keys ใน foreach นั้นโง่เมื่อคุณสามารถเรียกใช้ $ key => $ value และตรวจสอบคีย์ได้ เหตุใดจึงสร้างจุดข้อมูลใหม่ เมื่อคุณตัดทอนอาเรย์คีย์คุณจะใช้หน่วยความจำเพิ่มขึ้นทันที


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 มี builtin สำหรับสิ่งนั้นคุณจะไม่สามารถทำได้ในเวลาน้อยกว่า 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เป็นที่น่ากลัวและ verbose ฟรี
Mark Amery
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.