「ほっ」と。キャンペーン

[PHP] array_splice で配列の要素を削除する

わかってしまえば、なぁんだってことなのだけど、すごく悩んだので。

php の配列は(見た目は) 2 種類。

hash ぽいものと、array ぽいもの。
// array ぽい
$input_a = array("red", "green", "blue", "yellow");
// hash ぽい
$input_h = array("r"=>"red", "g"=>"green", "b"=>"blue", "y"=>"yellow");


配列から要素を削除する場合、
unset を使いなさいと書かれている。
「php 配列 要素 削除」で検索しても、大体 unset しか出てこない。
マニュアルにはこんな風に。
// 配列の要素の一つを破棄する
unset ($bar['quux']);


これは hash のときに有効なので、以下は有効。
// $input_h['r'] から "red" を取り出せなくなる
unset ($input_h['r']);


じゃ、これは array の方にも有効なんだろうか。
array に見えるものも、
実は中身はインデックスを添え字とした hash になっている。
上に、配列は見た目 2 種類と書いたのはこういうわけで、
実は 1 種類しかない。
つまり以下の 2 つは内部的に等価。
// こんな風に宣言しても
$input_a = array("red", "green", "blue", "yellow");
// 内部では、こんな風に宣言されたのと同じ
$input_a = array(0=>"red", 1=>"green", 2=>"blue", 3=>"yellow");


ってことは、当然 unset で消せる。
// "red" が消える
unset ($input_a[0]);

確かに消えるよ。

しかし、通常 array ぽい宣言をした方は、
順序が保証されていると(頭の中で)思って使うので、
0 から順に値を取り出してみると、
次のようなエラーが出る。
// 値を取り出してみる
for($i=0 ; $i<count($input_a) ; $i++) {
 echo $input_a[$i];
}

PHP Notice: Undefined offset: 0 in test.php on line **


なんでかっつーと、$input_a[0] を消したので、
0 というキーがなくなっちゃって、アクセスできなくなってるのね。
そうなの?と思いつつ、キーを表示してみると、こんな感じ。
// hash キーをカンマ区切りで表示してみる
implode (',', array_keys($input_a));

-> 1,2,3

あらホントに 0 が消えてるわよ。

じゃ、array の場合、キーも値も一緒に消すにはどうしたらいいか。
つまり、値もキーも消して、キーとなっている添え字を詰めて欲しい。
すごく探し回って array_splice を使ってみたら、うまくいった。
// 先頭の要素を 1 つ消す
array_splice($input_a, 0, 1);
implode (',', array_keys($input_a));

-> 0,1,2

おお消えた!
ま、先頭1コなら、通常popを使っちゃうけどね。

なんでこんなことをしたかったのかというと、
3つの要素を持つ配列があって、
その要素が更に配列。
図にすると、こんな感じ。
(ここからは等幅フォントで見てね)

元の配列。
┌─────────┬─────────┬─────────┐
│┌─┬─┬─┬─┐│┌─┬─┬─┬─┐│┌─┬─┬─┬─┐│
││A │B │C │D │││E │F │G │H │││I │J │K │L ││
│└─┴─┴─┴─┘│└─┴─┴─┴─┘│└─┴─┴─┴─┘│
└─────────┴─────────┴─────────┘

各要素の第1要素である A と E と I を比較して、
この配列をソートしたかった。

例えば、上の元の配列から B を unset で削除するとこうなる。
┌─────────┬─────────┬─────────┐
│┌─┬─┬─┬─┐│┌─┬─┬─┬─┐│┌─┬─┬─┬─┐│
││A │  │C │D │││E │F │G │H │││I │J │K │L ││
│└─┴─┴─┴─┘│└─┴─┴─┴─┘│└─┴─┴─┴─┘│
└─────────┴─────────┴─────────┘

第1要素である A E I はそのままだから、
A E I でソートしてもエラーは出ない。

ところが、元の配列から E を unset で削除するとこうなる。
┌─────────┬─────────┬─────────┐
│┌─┬─┬─┬─┐│┌─┬─┬─┬─┐│┌─┬─┬─┬─┐│
││A │B │C │D │││  │F │G │H │││I │J │K │L ││
│└─┴─┴─┴─┘│└─┴─┴─┴─┘│└─┴─┴─┴─┘│
└─────────┴─────────┴─────────┘

ソートするためのキーである第1要素がないので、
A I のところは大丈夫だけど、E があったはずのところところでエラーになる。

なので、E を削除して、更に要素も詰めて、第1要素でソートするためには、
配列は、こうなっていて欲しい。
┌─────────┬───────┬─────────┐
│┌─┬─┬─┬─┐│┌─┬─┬─┐│┌─┬─┬─┬─┐│
││A │B │C │D │││F │G │H │││I │J │K │L ││
│└─┴─┴─┴─┘│└─┴─┴─┘│└─┴─┴─┴─┘│
└─────────┴───────┴─────────┘

このために、 array_splice を使うのね、という話でした。


第1要素がなかったら、次の要素を見る、という風に書いてもよかったんだけど、
それは違うよなぁ、なんか方法あるでしょ常考、という気がしていたのよね。
文字で説明するとややこしいなぁ。

まとめるとこんな感じ。
  • array も hash も中身は hash
  • hash から要素を削除するのは unset
  • array から要素を削除するのは array_splice


こんな簡単なことに数時間悩んでしまった。
なわけで「php 配列 要素 削除」で検索したときに、
array_splice も引っかかったらいいなーと思いつつ、
この記事を書いてみたYO!

LL に限って言えば、
array や hash は、言語によって実装が違うので、
内部がどうなっているか考えながら書かないとなぁ。
つか、この辺がわかると、
その言語の結構な部分が理解できるんじゃないかと思っている。
javascript は object 自体が全部 hash 、とかそういうところ。
かなり言語の特色が出る部分じゃないかと。
[PR]
Commented by PHP5.3まだー at 2008-10-18 12:47 x
うーん。つーかPHPのは「ハッシュ」じゃなくて「連想配列」だよね。ハッシュ関数通してないし。
たまに、PHPは順番は保証されるのに、Perlは順番がぐちゃぐちゃになる→だからPerlは駄目言語・・・とか大威張りで言ってる厨房がいるけど、PHPの「連想配列」はシーケンシャルサーチだから遅いのよと小一時間・・
Commented by xiaoxia at 2008-10-20 12:36
●PHP5.3まだー さま
いらっしゃいませ。
仰言るとおり「連想配列」ですね、PHPのそれは。
順番が保証されるとかそういう点は、「仕様」だと思っています。
「そういうもの」というだけですよね。
どの言語にも、得意な処理も不得手な処理もあるわけで、使う方がそれをわかって使えば良いと思うのですよ。
それを良い悪いとジャッジするのって無意味じゃないかと。
ま、可愛いからみんなにもファンになって欲しい!という心理だと思うので、PHP使いとPerl使いの争いも、可愛いじゃんプログラマたち、と思ったりします(^-^)
Commented by (´-ω-`) at 2008-11-07 16:13 x
ちょうど要素1個配列から消すのにunset使えたかどうかをグーグル先生にきいたところで行きついたのでついでにコメント。(通りすがりの女PGです)

array_spliceはindex指定しなきゃいかんので面倒だなぁと。
結論いつも一回implodeして文字列に吐いてからexplodeで切りなおすという開き直りをしてます。
これだと歯抜け状態でも問題ないので。(抜けないでnullったりされると厄介だけど)

unset($array[4]);
$array = explode(',',implode($array));

的な。
まぁ、ただの戯言です。

Commented by xiaoxia at 2008-11-07 17:45
●(´-ω-`)さま <-かわいい♥
女PGナカーマですね(^-^) いらっしゃいませ。
文字列がキーの場合はarray_spliceは使えないようですので、数字がキーになっている場合を指しているのだと思います。数字がキーになっている場合は、例に挙げられた4のように、unsetの引数もindexで指定しなくてはいけませんよね? その場合はarray_spliceとあまり変わらないような気がするのですが…。しかし、unsetのほうが見た目のシンプルさは格段に上で、可読性が高いですね。
年寄りなもので、内部的にはarray_spliceの方がポインタを張り替えるだけなので軽くて速そう、なんて思ってしまいます。そんなこと考えてphp使う人も少ないでしょうね(^^;;
Commented by sata at 2008-11-12 19:15 x
お久しぶりでまたたどりつきました。

しかも前回コメント時から、職場が変わりました。(ははは)

またたどりつきましたら米します。
Commented by xiaoxia at 2008-11-14 18:32
●sataさま
perlのGDモジュールの時の方ですねー。お久しぶりです。
新しい職場に行かれたとのこと、すごいですね!色々ご苦労もあると思いますが、体に気をつけて(特にこの業界はw)頑張ってくださいね!
Commented by OK.2nd at 2009-12-26 10:12 x
array_splice()とunset()の違い、参考になりました。
Commented by xiaoxia at 2009-12-28 17:00
■OK.2ndさま お役に立てて良かったです(^-^)
Commented by にゃほと at 2010-03-05 14:32 x
なるほど~♪
勉強になりました。
ありがとうございます!!
Commented by xiaoxia at 2010-03-05 20:02
●にゃほとさま
それはようございました(^-^)
そう言っていただけると書いたかいがありました。
Commented by おおお at 2010-08-19 00:06 x
は?

女プログラマーって。

男とか女とか関係あんのかよ。

女だからすごいって言いたいの?
Commented by 通りすがり at 2010-08-19 19:17 x
勉強になりました。
でも最近はループ内の配列アクセスはforeachを使うので、配列の数字番号を意識することはあまりなくなってしまったんですけどね (^^;
Commented by xiaoxia at 2010-08-20 09:52
●おおおさま
某巨大掲示板あたりですと、女プログラマは使えない、という評価が多いように感じています。現場のプログラマにしては年齢も高いですし。
むしろ自虐的な意味をこめていますよ。

●通りすがりさま
そうですね、通常、配列の添え字を意識しなくても済みますよね。
しかし、この記事に書いたように、配列が入れ子になっていて、先頭要素をキーにしたソートしたい場合は、普段意識しない配列番号を意識しなくてはいけなくて、そのあたりが盲点でしたよ。
Commented by keigo at 2011-05-05 07:47 x
とても参考になりました。ありがとうございました。
Commented by xiaoxia at 2011-05-09 10:31
●keigoさま
お役に立てて何よりです(^-^) 書いたかいがあります
Commented by 男プログラマー at 2011-05-27 16:08 x
たすかりMAX
ありがとうございますm(_ _)m
Commented by xiaoxia at 2011-05-30 09:47
●男プログラマーさま
コメントいただきましてありがとウサギ(^-^)
Commented by ブレニー at 2011-11-02 10:19 x
配列からの要素削除を調べていて、このサイトにきました
私は結局array_diffという関数で実現しました
第1引数に元となる配列、第2引数に削除したい配列です
添字を指定しなくていいです、ご参考まで。
Commented by xiaoxia at 2011-11-04 09:02
●ブレニーさま
情報有難うございました。
集合の差分を取ることで削除するのですね。
なるほど発想の転換ですねー。
Commented by ケイ at 2011-12-02 11:01 x
ずばり、やりたかったことが説明されていて助かりました。
array_diffも良さそうですが、自分も年寄りなので、削除したい要素のインデックスがわかっているときは、コピーを作らない、array_spliceの方がすっきりしますね。(^^;
Commented by xiaoxia at 2011-12-02 18:13
■ケイさま
お役に立てて私も嬉しいです(^-^)v
内部でどういう動きをしているのか、
ちょっと考えちゃうんですよねー私も(笑)
インデックス張りかえてるのかなーとか、
別領域にいっぺんコピーしてるのかなーとか。
マシン環境がリッチな時代になりましたので、
その程度の領域だの処理速度だのは
たいした問題ではないのだろうとは思うのですがf(^^;;
by xiaoxia | 2008-09-08 12:54 | プログラム言語 | Comments(21)

ダメ女プログラマ&主婦&腐女子&バイオリン弾き


by 小霞