PHP 沒有真正的數組!

2021-01-07 CSDN

@程式設計師,一文搞懂 PHP 數組的真正用法。

作者 | Jeremy Ruten

譯者 | 彎月,責編 | 郭芮

以下為譯文:

PHP的數組非常多變。你可以把它當作映射使用,也可以當作集合使用,甚至可以當作數據對象使用……如果你很勇敢,甚至可以把它當作數組使用!

似乎將PHP數組當作傳統的數組(即一個擁有從0開始的整數索引列表)是一件很危險的事情,其中到處都是陷阱,還很容易製造出bug。這一切的原因都是因為PHP的數組並不是真正的數組。

我們來看看這究竟是怎麼回事。下面是一個PHP的小問題:下列程序的輸出是什麼?

<?php$letters = ['d', 'c', 'a', 'b', 'e'];$sorted = $letters;natsort($sorted);print $sorted[0];

答案是:它會輸出d!

這段代碼的本意是輸出按字母排序後的第一個字符串,應該是a。但$sorted[0]的意思並不是「數組中的第一個元素」。它的意思是「查找鍵0然後返回該鍵的元素」。

在PHP中,數組實際上是映射(即關聯數組),它有鍵和值。當你使用下面的代碼創建數組時,

$ary = ['a', 'b', 'c'];

實際的操作其實是:

$ary = [0 => 'a', 1 => 'b', 2 => 'c'];

PHP會給每個元素指定一個整數鍵,從0開始依次排列,因此在許多方面這個數組的行為跟傳統的數組很相似。

下面,我們嘗試一下鍵的順序:

<?php$ary = [1 => 'b', 2 => 'c', 0 => 'a'];print $ary[0]; // 'a'print $ary[1]; // 'b'print $ary[2]; // 'c'// Prints: b c aforeach ($ary as $letter) {print $letter;}// Prints: 1:b 2:c 0:aforeach ($ary as $key => $letter) {print"$key:$letter";}

如果通過鍵來訪問數組,那麼其行為跟傳統數組很相似。但在迭代數組時,它就不會關心鍵的順序,而是按照數組元素定義的順序:b,c,a。

所以,所有PHP數組都有鍵和值,但這些鍵值對還有特定的順序。換句話說:「PHP數組實際上是有序的映射。」

回到第一個示例:natsort()改變了鍵值對的順序,但沒有改變實際的鍵:

<?php$letters = ['d', 'c', 'a', 'b', 'e'];$sorted = $letters;natsort($sorted);print_r($letters); // [0 => 'd', 1 => 'c', 2 => 'a', 3 => 'b', 4 => 'e']print_r($sorted); // [2 => 'a', 3 => 'b', 1 => 'c', 0 => 'd', 4 => 'e']print $sorted[0]; //'d'

如果$sorted[0]不會訪問數組中的第一個元素,那麼怎樣才能訪問第一個元素呢?或者說,怎樣才能訪問前三個元素呢?方法有以下幾種。

array_values()

你可以通過array_values()給數組「重新編號」。它會返回一個數組,其值與傳入的數組相同,但鍵從零開始重新排列:

<?php$sorted = [2 => 'a', 3 => 'b', 1 => 'c', 0 => 'd', 4 => 'e'];$sorted = array_values($sorted);print $sorted[0]; // 'a'print $sorted[1]; // 'b'print $sorted[2]; // 'c'

array_slice()

通常,利用索引訪問PHP數組時,我們實際上查找的是一個鍵。但array_slice()是一個例外——傳入的索引實際上是數組內部的位置,而不是鍵。

<?php$sorted = [2 => 'a', 3 => 'b', 1 => 'c', 0 => 'd', 4 => 'e'];$first_three = array_slice($sorted, 0, 3);print $first_three[0]; // 'a'print $first_three[1]; // 'b'print $first_three[2]; // 'c'

這種做法的一個陷阱就是它依然會保留字符串鍵,這一點與array_values()不同。

reset()、next()及其他

PHP中的每個數組都有一個內部指針,可以用來迭代數組。reset()將內部指針重置到數組的開頭。next()將指針前進到下一個鍵值對。還有prev()、key()、current()和end()。這些低階API很難看,但能讓你理解PHP數組真正的工作原理:

<?php$sorted = [2 => 'a', 3 => 'b', 1 => 'c', 0 => 'd', 4 => 'e'];// start at the beginningprint reset($sorted); // 'a'print key($sorted); // 2print current($sorted); // 'a'// advance forwardprint next($sorted); // 'b'print key($sorted); // 3print current($sorted); // 'b'// advance forwardprint next($sorted); // 'c'print key($sorted); // 1print current($sorted); // 'c'// jump to the endprint end($sorted); // 'e'print key($sorted); // 4print current($sorted); // 'e'// there is no next element, so this returns nullprint next($sorted);

你幾乎永遠不需要使用這些函數,但有時候可以使用reset()來方便地獲取數組的第一個值,或者用end()獲取最後一個值。

Laravel的collect()

Laravel有一個非常好用的collections類,該類已被提取到一個獨立的函數庫中。我們的工作中就在使用這個函數庫。

它提供了first()和last()方法來獲取數組的第一個和最後一個元素,以及take()方法獲取前N個元素。

<?php$sorted = [2 => 'a', 3 => 'b', 1 => 'c', 0 => 'd', 4 => 'e'];print collect($sorted)->first(); // 'a'print collect($sorted)->last(); // 'e'print_r(collect($sorted)->take(3)->all()); // [2 => 'a', 3 => 'b', 1 => 'c']

注意take()會保留數組中的鍵。許多PHP數組函數都接受一個可選的參數$preserve_keys。而這個collections函數庫在封裝PHP數組函數時,幾乎永遠都會給這個參數傳遞true,所以可以認為使用該函數庫時,鍵一直都被保留。

使用PHP數組的幾個建議

下面是我在使用PHP數組時總結的一些心得:

時刻小心$ary[0]

每當你看到數組索引為0時,就應該知道這段代碼很可能在將PHP數組當作傳統數組處理。通常這種假設是不安全的,特別是當數組是從其他地方傳遞過來的情況下。0也許並不是數組中的第一個鍵,甚至可能根本不存在(例如在執行了array_filter()之後)。

除非是你自己創建的數組,否則應該使用reset($ary)或者庫函數,比如collect($ary)->first()方法。

考慮對數組重新編號

$ary[0]問題也可以通過另一種方式解決:每當給其他函數傳遞數組時,確保它的鍵像傳統數組一樣。通常這意味著在執行array_filter()或者任何可能刪除鍵或改變鍵的順序的函數之後需要調用array_values()。

你可以說,利用array_values()反覆「修正」數組違反了數組本身的性質。或許,我們不應該讓PHP數組表現得像傳統數組,而是應該接受PHP數組的性質,寫代碼時不要對數組的鍵做出假設。然而新手很可能會認為PHP數組像傳統數組一樣。

上面兩點遵循了健壯性原則:「發送時要保守,接收時要自由。」

比較數組時,鍵很重要,但順序不重要

像assertEquals(['a', 'b'], array_filter(['a', null, 'b']))之類的比較會失敗,因為[0 => 'a', 1 => 'b']並不等於[0 => 'a', 2=> 'b']!它們的鍵必須一樣才行。

相反,assertEquals($ary, collect($ary)->sort()->all())永遠為真,因為對於==來說,鍵值對的順序並不重要。例如,[0 => 'a', 1 => 'b'] == [1 => 'b', 0 => 'a']為真。

將數組當作映射使用

假設你有一個$users_by_id數組,其中$users_by_id[32]會查找id為32的User。那麼,如果使用array_map(function ($user) { return $user->getName(); })來映射該數組,則會得到一個name的數組,其鍵為user id,因為array_map()並不會改變數組的鍵。在這種情況下保留鍵非常有用,所以當需要映射時,你應該避免使用可能導致數組重新編號的函數。

將數組當作有序映射使用

可以對$users_by_id數組進行排序,然後利用它:(1)根據id查找用戶;(2)按照特定順序遍歷所有用戶。利用這個技巧,就不需要像某些其他語言那樣,僅僅為了跟蹤順序而維護一個單獨的id數組。

指示數組的類型

PHP數組造成的許多混亂都是因為它試圖將一種數據結構用於兩種目的。儘管PHP的類型系統不會區別傳統數組和映射,但我們應該區分。由於缺少合適的類型系統,我們應當使用變量命名規則來指示類型。例如,傳統數組通常用複數形式,如$users,而映射通常以「by x」結尾,如$users_by_id。

總結

思維模型非常重要。我曾經把PHP數組只當作鍵值對的集合,但後來我意識到,數組實際上是鍵值對的有序列表,這時我才真正理解了PHP數組。

關於PHP文檔中的數組和數組函數的怪異行為還有許多需要學習的地方,我發現有了正確的思維模型才能理解這些怪異的行為!

原文:https://medium.com/7shifts-engineering-blog/php-arrays-arent-really-arrays-57b627a1e46a

本文為 CSDN 翻譯,轉載請註明來源出處。

相關焦點

  • 巧用 PHP 數組函數
    php$input = ['hello' => 'world', 0 => 233, 99 => 666];var_dump(array_slice($input, 0));array_slice 函數的功能是取出數組的中的一段,但它默認會重新排序並重置數組的數字索引,所以可以利用它重置數組中的數字索引。
  • PHP數組使用之道(乾貨)
    php$numbers = [-1, 0, 1];$not_empty = array_filter($numbers);print_r($not_empty);你可以使用 array_unique() 函數用於從數組中獲取唯一值元素。注意該函數會保留唯一元素在原數組中的鍵名:<?
  • PHP如何刪除數組中的重複元素 - php中文網
    1.array_unique()函數array_unique()函數可以移除數組中的重複的值,並返回結果數組;當幾個數組元素的值相等時,只保留第一個元素,其他的元素被刪除。代碼示例:<?php$result1 = array("a" => "green", "red", "b" => "green", "blue", "red");var_dump($result1);$result2 = array_unique($result1);var_dump($result2
  • PHP數組的處理方法
    > 3、統計數組的長度:count():  count函數有兩個參數:  0(或COUNT_NORMAL)為默認,不檢測多維數組(數組中的數組);  1(或COUNT_RECURSIVE)為檢測多維數組  $arr=array(  0=>array('title' => '蘋果', 'viewnum'
  • 曬碼姐講堂-PHP數組
    今天我們來學習的就是php的數組,他是一個重要的編程結構,以前咱們的變量都是標量變量,只寫變量只能存儲單個的數值 .數組是可以存儲一組或者很多數值的變量.一個數組可以存儲很多元素,每個元素可以有一個值,例如文本,數字或另一個數組.一個包含其他數組的數組是多維數組.我記得上大學的時候,我們老師大學老師曾經說過,數學是思維的舞蹈,我對這句話特有感情.
  • PHP中數組元素升序、降序及重新排序的函數
    首頁 > 語言 > 關鍵詞 > php最新資訊 > 正文 PHP中數組元素升序、降序及重新排序的函數
  • PHP數組函數有哪些?
    array_combine函數定義:array_combine() 函數通過合併兩個數組來創建一個新數組,其中的一個數組元素為鍵名,另一個數組的元素為鍵值。php$array = array( "php基礎"=>"php", "java基礎"=>"java", "jsp基礎"=>"jsp");echo key($array); echo "<br>";echo current($array); echo "<br>";echo next($array); echo "<br>";echo end
  • PHP數組讀取的循環操作
    首頁 > 語言 > 關鍵詞 > php最新資訊 > 正文 PHP數組讀取的循環操作
  • 在PHP中處理JSON數組以及對象
    數組和對象,以及如何在 PHP 中指定他們。特別是,問題是由空對象和數組對象引起的,此頁面將向您展示 Elasticsearch JSON API 中使用的一些常見模式,以及如何將其轉換為 PHP 的表現形式。空對象Elasticsearch API 在多個位置上使用空的 JSON 對象,這可能會給 PHP 帶來問題。與其他語言不同,PHP 沒有空對象的「簡短」表現形式。
  • (實用篇)PHP中unset,array_splice刪除數組中元素的區別
    以下是文章分享2群,由於群人數已超過300,不能掃碼進群,這個任務呢,就由小篇來拉你們進群了,掃描下面二維碼,加小篇好友~php中刪除數組元素是非常的簡單的,但有時刪除數組需要對索引進行一些排序要求我們會使用到相關的函數,這裡我們來介紹使用unset,array_splice刪除數組中的元素區別吧
  • PHP中的數組分頁實現(非資料庫)
    它的作用是從數組中截取出一段內容來並返回這段內容的數組。array_chunkarray_chunk() 函數則是根據一個數值參數將一個數組進行分組,也就是將數組分割成一段一段的子數組。我們就可以根據分割後的數組來獲取指定下標的子數組內容,這些內容就是當前的頁面需要展示的數據了。
  • php學習之數組的相關知識-注意事項
    1.數組的介紹說明:一組數據的集合,數組是由元素組成案例:求學生的總分數和平均分數常規的做法:<?php/*** @Author: 吾愛樂享* @Date: 2018-09-17 22:16:42* @Last Modified by: 吾愛樂享* @Last Modified time: 2018-09-17 22:33:03*///求學生的總分數和平均分數//常規的做法$yuwen = 70;$shuxue = 90;$yingyu = 50;$tiyu = 100;$sum
  • 你面試中,遇到的php數組的面試題,快收藏
    ,數組中的數為遞增的等比數,比值為3,首項為1.,數組中的元素滿足斐波拉契數列的規律.10個元素,請重新輸入7.將兩個數組連接成一個新數組.() 函數 一樣,將一個或多個數組的元素的合併起來,一個數組中的值附加在前一個數組的後面。
  • 【每天一題】PHP中常用的數組操作方法筆記整理
    PHP中擁有傳統的array數組結構,並且隨著版本的升級也在不斷改進,比如從php5.4起可以使用短數組定義語法、我們文中會講到,下面就來看一下PHP中常用的數組操作方法筆記整理:概述要訪問一個變量的內容,可以直接使用其名稱。
  • 用php代碼實現在數組中追加一個全新的元素與屬性
    本文主要簡單介紹了用php代碼在一個數組中添加一個全新的元素與屬性方法 1、首先要定義一個用於測試的正常數組
  • php str-ireplace()函數替換字符串/數組中的一些字符
    str_ireplace()函數定義及用法在php中,str_ireplace()函數是使用來替換字符串或數組中的一些字符(不區分大小寫),返回替換後的新字符串或數組,原字符串或數組不變;str_ireplace()函數如果處理的數據是一個數組:該函數將對數組中的每個元素進行查找和替換
  • PHP數組遍歷的幾種方式
    value}遍歷原理對於php來說,每一個數組中,都有一個「內置的」指針,通常它指向這個數組的某個單元,圖示如下:舉例:$arr =(新單元)上的值$v2 = prev( $arr ); //將數組的指針移動到「上一個單元」,並取得這個新位置(新單元)上的值$v3 = current( $arr ); //直接取得數組的指針所在位置的單元的值;$v4 = key( $arr ); //直接取得數組的指針所在位置的單元的鍵
  • (實用篇)PHP JSON數組與對象的理解
    以下是文章分享1群,由於群人數已超過300,不能掃碼進群,這個任務呢,就由小篇來拉你們進群了,掃描下面二維碼,加小篇好友~在PHP後端和客戶端數據交互的過程中,JSON數據中有時格式不定,一會兒是數組,一會兒是對象,弄得客戶端開發人員要崩潰的感覺。
  • 探討如何實現PHP刪除數組元素
    對於學習PHP語言的人來說,在數組上添加元素是一件非常輕鬆的事情。那麼,當我們需要刪除數組元素的時候,應該如何做呢?其實這一步驟同樣很簡單。下面我們就來為大家介紹有關PHP刪除數組元素的具體實現方式。
  • 基礎,總結php把字符串轉為數組的方法
    實現代碼:結果:str_split — 將字符串轉換為數組str_split() 函數把字符串分割到數組中。如果 length大於字符串的長度,則整個字符串將作為數組的唯一元素返回。案例,php實現複製目錄和子目錄的方法