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

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

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

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

Q&A

解決済

3回答

13862閲覧

二つの多次元配列を比較して、片方にだけ含まれるものを抽出する方法

zico_teratail

総合スコア907

PHP

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

1グッド

3クリップ

投稿2017/01/07 14:14

編集2017/01/07 14:16

二つの多次元配列の片方にだけ含まれるものを抽出したいです。

具体的には、以下のような配列Aと配列Bを比べて、片方にだけ含まれる組み合わせ、すなわちこの場合は
[flag] => 88
[status] => 25

[flag] => 122
[status] => 25
の組み合わせが、最終的に得たい結果です。

php

1配列A 2Array 3( 4 [0] => Array 5 ( 6 [flag] => 2 7 [status] => 25 8 ) 9 10 [1] => Array 11 ( 12 [flag] => 64 13 [status] => 25 14 ) 15 16) 17 18配列B 19Array 20( 21 [0] => Array 22 ( 23 [flag] => 122 24 [status] => 25 25 ) 26 [1] => Array 27 ( 28 [flag] => 2 29 [status] => 25 30 ) 31 32 [2] => Array 33 ( 34 [flag] => 64 35 [status] => 25 36 ) 37 38 [3] => Array 39 ( 40 [flag] => 88 41 [status] => 25 42 ) 43 44) 45

試したコードは以下の通り。

php

1<?php 2//配列A 3$ArrayA[] = array('flag'=>'2','status'=>'25'); 4$ArrayA[] = array('flag'=>'64','status'=>'25'); 5 6//配列B 7$ArrayB[] = array('flag'=>'122','status'=>'25'); 8$ArrayB[] = array('flag'=>'2','status'=>'25'); 9$ArrayB[] = array('flag'=>'64','status'=>'25'); 10$ArrayB[] = array('flag'=>'88','status'=>'25'); 11 12 13 14foreach ($ArrayB as $key => $valarr) { 15 16 foreach ( $ArrayA as $key2 => $valarr2) { 17 $returnArray[] = array_diff_assoc($valarr, $valarr2); 18 } 19} 20 21echo "<pre>"; 22print_r($returnArray); 23echo "</pre>";

差分をとるからdiffかなと思ったのですが、上記コードの実行結果は下記の通りで、意図とはだいぶ違いました。

php

1Array 2( 3 [0] => Array 4 ( 5 [flag] => 122 6 ) 7 8 [1] => Array 9 ( 10 [flag] => 122 11 ) 12 13 [2] => Array 14 ( 15 ) 16 17 [3] => Array 18 ( 19 [flag] => 2 20 ) 21 22 [4] => Array 23 ( 24 [flag] => 88 25 ) 26 27 [5] => Array 28 ( 29 [flag] => 88 30 ) 31 32 [6] => Array 33 ( 34 [flag] => 64 35 ) 36 37 [7] => Array 38 ( 39 ) 40 41)

冒頭に挙げたようなflagとstatusの組み合わせを持つ要素だけを抽出するにはどうしたらいいでしょうか?

xekid👍を押しています

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

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

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

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

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

guest

回答3

0

PHP7以降ならこう書くのが一番いいです。

php

1<?php 2 3$arr1 = [ 4 ['flag' => 2, 'status' => 25], 5 ['flag' => 64, 'status' => 25], 6]; 7$arr2 = [ 8 ['flag' => 122, 'status' => 25], 9 ['flag' => 2, 'status' => 25], 10 ['flag' => 88, 'status' => 25], 11]; 12 13$compare = function ($x, $y) { 14 return $x['flag'] <=> $y['flag'] ?: $x['status'] <=> $y['status']; 15}; 16 17var_dump(array_merge( 18 array_udiff($arr2, $arr1, $compare), 19 array_udiff($arr1, $arr2, $compare) 20));

投稿2017/01/08 03:07

mpyw

総合スコア5223

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

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

zico_teratail

2017/01/08 17:10

ありがとうございます。 php7についての知識がないうえ、コードが難解かつ高度すぎて私には全然読めません。 知らない記法がいくつかあるし、内容が理解できません・・・ でもpaizaで実行してみたら、たしかに希望の結果が得られました。
mpyw

2017/01/09 02:47

if ($a < $b) return -1; if ($a > $b) return 1; return 0; これの省略系が return $a <=> $b; です。また, return $a ? $a : $b; の省略形が return $a ?: $b; です。要するに先述のコードは 「flagで比較して,もし同じだったらstatusでも比較して,それも同じだったら0を返して,array_udiffに等しいと見なさせる。それ以外の場合は-1か1を返して異なると見なさせる。」 という動作になりますね。この書き方は PHP7で宇宙船演算子を使いこなすぞ - Qiita http://qiita.com/Hiraku/items/62821d7d0e7af1ac211e で紹介されています。
guest

0

ベストアンサー

このように行ってみるのはいかがですか?

PHP

1<?php 2$arr1 = array( 3 array( 4 "flag" => 2, 5 "status" => 25 6 ), 7 array( 8 "flag" => 64, 9 "status" => 25 10 ) 11); 12$arr2 = array( 13 array( 14 "flag" => 122, 15 "status" => 25 16 ), 17 array( 18 "flag" => 2, 19 "status" => 25 20 ), 21 array( 22 "flag" => 64, 23 "status" => 25 24 ), 25 array( 26 "flag" => 88, 27 "status" => 25 28 ) 29); 30$array_diff = array_udiff($arr2, $arr1, function ($array1, $array2) { 31 $result1 = $array1['flag'] - $array2['flag']; 32 $result2 = $array1['status'] - $array2['status']; 33 return $result1 + $result2; 34}); 35print_r($array_diff);

投稿2017/01/07 14:53

編集2017/01/07 15:24
s8_chu

総合スコア14731

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

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

zico_teratail

2017/01/07 15:00

ありがとうございます。 試してみましたが、結果の配列は空でした。 Array ( )
s8_chu

2017/01/07 15:24

失礼しました。修正しました。
zico_teratail

2017/01/07 16:41

ありがとうございます。 たしかに目的の結果が得られました! しかし自分がアホなため、なぜこのコールバック関数で目的の結果が得られたのか、イマイチ原理というか挙動を理解できておりません。 もし出来ましたら、仕組みを解説をしていただけましたら幸いです。
s8_chu

2017/01/07 17:12

array_diffの比較方法をクロージャで指定できるarray_udiffというものを使っています。クロージャではarr2のflagとarr1のflagで一致する値を取り除き、arr2のstatusとarr1のstatusで一致する値を取り除き残りを変数result1, 2にそれぞれ代入して、それを結合して返しています。
zico_teratail

2017/01/07 17:22 編集

解説ありがとうございます。 まだ自分の理解不足でよくわかっていないのですが、その方法ですとflagとstatusをそれぞれ別個に処理しているということですよね。でもそれだと、「flagとstatusの組み合わせ」が崩れたりしませんか? たとえば質問文の例ではstatusが全て「25」なので問題にはなりませんが、もしstatusの値が全て異なっていた場合、「flagとstatusの組み合わせ」が維持されないような気が・・・
zico_teratail

2017/01/07 17:25

すみません、statusの値をいろいろ変えてみて実験してみましたが、きちんと「flagとstatusの組み合わせ」が維持された結果が返ってくるようです。 でもどうしてあのコールバック関数でそのような挙動になるのか、自分の能力が低すぎて全然理解できません・・・
mpyw

2017/01/08 03:11 編集

このコードは「片方にだけ含まれる組み合わせ」の条件を満たしていません,得られるのは「$arr2のみに含まれる組み合わせ」のみです。またコールバック関数の実装にもバグがあり,例えば$flagと$statusの計算結果がそれぞれ-2と+2のようになったとき,同じと見なされてしまいます。
zico_teratail

2017/01/08 16:47

>mpywさん たしかにそうですね。 ただ今回のケースでは要素に含まれるのは正の値のみなので、そういう点では問題ないかもしれません。
guest

0

これでも一応。

PHP

1//配列A 2$ArrayA[] = array('flag'=>'2','status'=>'25'); 3$ArrayA[] = array('flag'=>'64','status'=>'25'); 4 5//配列B 6$ArrayB[] = array('flag'=>'122','status'=>'25'); 7$ArrayB[] = array('flag'=>'2','status'=>'25'); 8$ArrayB[] = array('flag'=>'64','status'=>'25'); 9$ArrayB[] = array('flag'=>'88','status'=>'25'); 10 11$returnArray = array_filter( $ArrayB, function( $item ) use( $ArrayA ) { 12 $item = json_encode( $item ); 13 foreach ( $ArrayA as $value ) { 14 if ( $item === json_encode( $value ) ) { return false; } 15 } 16 return true; 17} ); 18 19echo "<pre>"; 20print_r($returnArray); 21echo "</pre>"; 22/* 23Array 24( 25 [0] => Array 26 ( 27 [flag] => 122 28 [status] => 25 29 ) 30 31 [3] => Array 32 ( 33 [flag] => 88 34 [status] => 25 35 ) 36 37) 38*/

追記:

書き直しました。

PHP

1$returnArray = array_filter( $ArrayB, function( $item ) use( $ArrayA ) { 2 foreach ( $ArrayA as $value ) { 3 if ( 0 === count( array_diff( $item, $value ) ) ) { return false; } 4 } 5 return true; 6} );

投稿2017/01/07 15:02

編集2017/01/07 20:01
kei344

総合スコア69407

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

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

zico_teratail

2017/01/07 16:43

ありがとうございます。 こちらのご回答に関しても自分の能力不足のため、なぜそのコールバック関数で目的の結果が得られるのか、論理がよく分かりませんでした。 特にjson_encodeが出てきた辺りがチンプンカンプンです・・・
kei344

2017/01/07 17:26

array_filter は配列をフィルタリングするための関数で、コールバック関数に配列の要素をそれぞれ1つずつ渡してくれます。 それを json_encode したものと比較対象の配列の要素を json_encode したものと比較して、同じものがあれば false を返すことでフィルタ(排除)出来ます。 【[ PHP ] 配列の要素をフィルタリングする ( array_filter ) – 行け!偏差値40プログラマー】 http://hensa40.cutegirl.jp/archives/1515 JSONはPHPの配列/オブジェクトを「'{"flag":"122","status":"25"}'」のようなテキストで表現するフォーマットなので配列がどのような形であっても比較しやすいかな、と思って書いてみました。 すみません、本当は json_encode では順番が保障されていないはずなので上手くいかないこともある可能性があります。(「'{"flag":"122","status":"25"}'」と「'{"status":"25","flag":"122"}'」をテキスト比較しても当然違う)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問