質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

3回答

314閲覧

配列の特定の要素を探して同じ配列ないの他の要素を出力する

SugiuraY

総合スコア317

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

0クリップ

投稿2019/09/29 05:04

編集2019/09/29 05:05

[目的]
下記のように2つの配列があり、$listの各要素に一致するような$ary[$j][0]がある場合に、$ary[$j]の他の要素をそれぞれ出力させたいと考えております。

[現状]
for文をネスト化することで下記のように、当然実装することができるのですが、2次元の配列をループさせており、実際にはどちらの配列も相当数があることが想定されるので、重い処理になってしまい、望ましい方法ではないと考えております。
また、自分なりに調べた結果phpには配列関連のin_array()やarray_search()と言った便利な関数があることがわかったのですが、これらを使用した場合に該当する要素が含まれていた場合にその要素が含まれる配列の他の要素を出力すると言ったアイデアが思いつきません。

[ご質問]
ループ処理のネスト化をさせずに配列関係の関数を利用して、該当する要素が含まれる配列の他の要素を出力がしたいのですが良い方法はありますでしょうか?。イメージとしては以下の通りです。

for ($i=0; $i <$count_1 ; $i++) {//ここは順番に処理するためループ処理
//ここでarray関係の関数を利用してネスト化させずに処理するが同時に該当する要素が含まれる配列の他の要素を出力がしたい
}

そもそもこの発想が適切ではなければ、根本的に異なるご提案でも結構ですので
ご意見を頂戴できると、大変幸いです。
よろしくお願い申し上げます。

php

1 2//$aryの中身 3Array 4( 5 [0] => Array 6 ( 7 [0] => 19 8 [1] => hoge 9 [2] => 空は青い 10 [3] => 2019-09-29 10:26:53 11 ) 12 13 [1] => Array 14 ( 15 [0] => 19 16 [1] => fuga 17 [2] => 山は緑 18 [3] => 2019-09-29 13:29:45 19 ) 20 21 [2] => Array 22 ( 23 [0] => 27 24 [1] => bar 25 [2] => 富士山は綺麗 26 [3] => 2019-09-29 10:45:35 27 ) 28 29)

php

1//該当する要素の配列の他の要素を出力 2$list=array(1,2,3,19,22,100,102); 3$count_1 =count($list); 4$count_2 =count($ary); 5for ($i=0; $i <$count_1 ; $i++) { 6 for ($j=0; $j <$count_1 ; $j++) { 7 if($list[$i]===$ary[$j][0]){ 8 echo $ary[$j][1]; 9 echo $ary[$j][2]; 10 echo $ary[$j][3]; 11 //$list[$i]===$ary[$j][0]を満たす$ary[$j]の他の要素を出力 12 } 13 } 14} 15

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

こういうことですか?
(配列 $ary1 から 「0 要素目 が $list に入っているもの」 だけを絞った配列を $ary2 として取り出した)

php

1$ary1 = [ 2 [ 19, 'hoge' , '空は青い' ], 3 [ 19, 'fuga' , '山は緑' ], 4 [ 27, 'bar' , '富士山は綺麗' ], 5]; 6 7$list = [1, 2, 3, 19, 22, 100, 102]; 8$ary2 = array_filter($ary1, function($a) use ($list) { return in_array($a[0], $list, TRUE); }); 9 10foreach ($ary2 as $a) { 11 echo $a[1] . "\n"; 12 echo $a[2] . "\n"; 13}

投稿2019/09/29 13:20

編集2019/09/29 13:23
tanishi_a

総合スコア484

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

SugiuraY

2019/09/30 01:15

コメントありがとうございます。ご教示いただいたarray_filterで実装できそうです、今他の方が処理速度等を含めてたくさんコメントをくださっているため、これらもじっくり検証して実装を検討していきたいと思います!
guest

0

array_fill_keys を使って、$list の値をキーとした配列を作り、array_key_exists でチェックすればループが一重になって早そうな気がします。
実装として、配列は添え字から値を検索するように最適化されているので、値が存在するかどうかは線形探索を使い、添え字が存在するかどうかはハッシュを使っていると推測されるからです。

実測はしていません。

投稿2019/09/29 23:24

Zuishin

総合スコア28656

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2019/09/29 23:30

データベースで言えば、添え字はインデックスが付けられたカラム、値は付けられていないカラムに相当するため、添え字で検索すれば内部でループせず速いのではないかという推測です。
Zuishin

2019/09/29 23:33

値にもインデックスが付けられていた場合はこの限りではありませんが、パフォーマンス上の理由から、その可能性は低いと見ています。
退会済みユーザー

退会済みユーザー

2019/09/29 23:42 編集

あー確かに。 数が多いなら in_array より array_key_exists の方が早そうですね。 ただ、検証とその考察してみたんですけど、php の配列、変なことやってそうです。そもそも、計算量と一致したパフォーマンスにならないんですよねぇ^^; 関連記事を拾い読みしてみましたが分かりませんでしたw
Zuishin

2019/09/29 23:50

なんと。やはり実際試してみなければいけなかったですね。今それができる環境にないので、後で調べてみます。
退会済みユーザー

退会済みユーザー

2019/09/30 00:15

早いのは間違いないですw paiza.io なんですげぇ揺れがありますが、一応結果を私の回答へ追加してみました。
SugiuraY

2019/09/30 01:11

ご回答有難うございます。そしてご返信遅くなり申し訳ございません。 現状、皆さんの回答がまだ私には高度すぎと少しずつ咀嚼して確認しております。 一旦、配列の取り方についてですが、RDB(MYSQL)からどちらの配列も取得してきているのですが、fetch(PDO::FETCH_NUM)で取得しているため、仰る通り、異なる取得の仕方はできそうです。 例えばですが、$aryについて [19] => Array ( [0] => hoge [1] => 空は青い [2] => 2019-09-29 10:26:53 ) [19] => Array ( [0] => fuga [1] => 山は緑 [2] => 2019-09-29 13:29:45 ) など他のFETCHパターンで添え字等を工夫できそうな気がします。
SugiuraY

2019/09/30 01:12

申し訳ございません、今すぐ気づいたのですが添え字にそれぞれの$ary[$i][0]の値をもってくると19のように実際重複する値が出てくるのでだめでしたね、、、失礼しました。
Zuishin

2019/09/30 01:19

コメントをつける回答を間違えていませんか?
Zuishin

2019/09/30 01:25

te2ji さん追記ありがとうございます。要素数が多いときに思ったほど差が出ませんね。内部で何か工夫してるっぽいですね。
SugiuraY

2019/09/30 02:00

コメントありがとうございます。 お二方でのやり取りもあったため、Zushin様側へのコメントとして記載をさせて頂きました。改めてその旨もte2j様のコメントにも付記させて頂きます。
退会済みユーザー

退会済みユーザー

2019/09/30 06:57

> Zuishin さん そうなんですよねぇ。。。普通に考えて結果がおかしいw ボリュームやバージョンで違いが出るっぽいので、本気でやるなら実測あるのみ(またはソースを読む)っぽいです。
guest

0

ベストアンサー

今の配列の持ち方だと効率化はできません。

もし$arrが DB から取り出した値なのであれば、取り出すときの構造を工夫すれば効率の良い取り出しができるかと。

追記
興味本位で、配列の持ち方の変更まで含めてパフォーマンスがどうなるか、コードを書いてみました。

php

1<?php 2function test_in_array($arr, $list){ 3 $res = []; 4 foreach($arr as $value){ 5 if(in_array($value[0], $list)){ 6 $res[] = $value; 7 } 8 } 9 return $res; 10} 11 12function test_foreach($arr, $list){ 13 $res = []; 14 foreach ($arr as $value){ 15 foreach($list as $num){ 16 if($value[0]===$num){ 17 $res[] = $value; 18 } 19 } 20 } 21 return $res; 22} 23 24function test_sort($arr, $list){ 25 $res = []; 26 foreach($arr as $value){ 27 if(!isset($tmp[$value[0]])){ 28 $tmp[$value[0]] = []; 29 } 30 $tmp[$value[0]][] = $value; 31 } 32 foreach($list as $num){ 33 if(isset($tmp[$num])){ 34 $res[] = $tmp[$num]; 35 } 36 } 37 return $res; 38} 39 40foreach([[10000, 10], [1000, 10], [10000, 100], [100000, 100],] as [$arr_max, $list_max]){ 41 $arr = []; 42 for($i = 0; $i < $arr_max; $i++){ 43 $arr[] = [ 44 rand(0, 100), 45 'hoge', 46 'fuga', 47 ]; 48 } 49 50 $list = []; 51 for($i = 0; $i < $list_max; $i++){ 52 $list[] = rand(0, 100); 53 } 54 echo 'test : arr_max = ' . $arr_max . ', list_max = ' . $list_max . PHP_EOL; 55 foreach(['test_in_array', 'test_foreach', 'test_sort'] as $func){ 56 $time_start = microtime(true); 57 $func($arr, $list); 58 $time = microtime(true) - $time_start; 59 printf("$func \t %.7f 秒\n",$time); 60 } 61}

in_array が圧倒的に早いかと思ってましたが、そうでもないですね。

php

1test : arr_max = 10000, list_max = 10 2test_in_array 0.00115303test_foreach 0.00193604test_sort 0.00108605test : arr_max = 1000, list_max = 10 6test_in_array 0.00003917test_foreach 0.00017218test_sort 0.00005419test : arr_max = 10000, list_max = 100 10test_in_array 0.001050011test_foreach 0.015294112test_sort 0.000724113test : arr_max = 100000, list_max = 100 14test_in_array 0.027310115test_foreach 0.174224116test_sort 0.0325580

やはり実測してみると面白いです。
paiza.io を使用したので計測値は結構揺れました。(timeout するんで、回数も絞ってますw)
多分、php のバージョンによっても異なるはずなので、自身の環境で確認してみてください。

さらに追記

php

1<?php 2function test_in_array($arr, $list){ 3 $res = []; 4 foreach($arr as $value){ 5 if(in_array($value[0], $list)){ 6 $res[] = $value; 7 } 8 } 9 return $res; 10} 11 12function test_array_key_exists($arr, $list){ 13 $list2 = array_fill_keys($list, TRUE); 14 $res = []; 15 foreach($arr as $value){ 16 if(array_key_exists($value[0], $list2)){ 17 $res[] = $value; 18 } 19 } 20 return $res; 21} 22 23function test_foreach($arr, $list){ 24 $res = []; 25 foreach ($arr as $value){ 26 foreach($list as $num){ 27 if($value[0]===$num){ 28 $res[] = $value; 29 } 30 } 31 } 32 return $res; 33} 34 35function test_sort($arr, $list){ 36 $res = []; 37 foreach($arr as $value){ 38 if(!isset($tmp[$value[0]])){ 39 $tmp[$value[0]] = []; 40 } 41 $tmp[$value[0]][] = $value; 42 } 43 foreach($list as $num){ 44 if(isset($tmp[$num])){ 45 $res[] = $tmp[$num]; 46 } 47 } 48 return $res; 49} 50 51foreach([[10000, 10], [1000, 10], [10000, 100], [100000, 100],] as [$arr_max, $list_max]){ 52 $arr = []; 53 for($i = 0; $i < $arr_max; $i++){ 54 $arr[] = [ 55 rand(0, 100), 56 'hoge', 57 'fuga', 58 ]; 59 } 60 61 $list = []; 62 for($i = 0; $i < $list_max; $i++){ 63 $list[] = rand(0, 100); 64 } 65 66 echo 'test : arr_max = ' . $arr_max . ', list_max = ' . $list_max . PHP_EOL; 67 foreach(['test_in_array', 'test_foreach', 'test_sort', 'test_array_key_exists'] as $func){ 68 $time_start = microtime(true); 69 $func($arr, $list); 70 $time = microtime(true) - $time_start; 71 printf("$func \t %.7f 秒\n",$time); 72 } 73}
test : arr_max = 10000, list_max = 10 test_in_array 0.0010860 秒 test_foreach 0.0018811 秒 test_sort 0.0021858 秒 test_array_key_exists 0.0003760 秒 test : arr_max = 1000, list_max = 10 test_in_array 0.0000389 秒 test_foreach 0.0001709 秒 test_sort 0.0000539 秒 test_array_key_exists 0.0000341 秒 test : arr_max = 10000, list_max = 100 test_in_array 0.0010271 秒 test_foreach 0.0149219 秒 test_sort 0.0006859 秒 test_array_key_exists 0.0004339 秒 test : arr_max = 100000, list_max = 100 test_in_array 0.0258038 秒 test_foreach 0.1712370 秒 test_sort 0.0291610 秒 test_array_key_exists 0.0179920 秒

結論:array_key_exists() は早い!w

投稿2019/09/29 11:40

編集2019/09/30 00:14
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

SugiuraY

2019/09/30 02:06

te2j様 コメントを頂き誠にありがとうございます。 上記Zushin様側にもコメントをさせて頂きましたが、今処理速度の検証は私には高度であるため、少しずつ動かしてみながら自分でも確認してみております。$tmpがどこから来たのかがわからずに少し躓いてしまっていますが。。 また、配列の持たせ方についても上記の通りPDOのFETCH::NUMでMYSQLから取得しているため、合わせて他の方法で取得することができないか(特に添え字に持ってくるのもを工夫する)を検討しております。 いずれにしても、非常に勉強になりそうなのでこの機会に頑張ってくらいついて検証してみます! 有難うございます!
退会済みユーザー

退会済みユーザー

2019/09/30 06:54

$tmp は配列を再構成したものです。テンポラリーです。 foreach($list as $num){ の直前で var_dump() してみると分かります。 DB からの取り出しは https://qiita.com/mpyw/items/d52351bd1a8068344cc2 が参考になるかと。 ただ、PDO 内部で何やってるか確認してないので、パフォーマンスは計測してみたほうが良いです。
SugiuraY

2019/10/02 12:51

ありがとうございます! 確認して理解することができました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問