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

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

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

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

Q&A

解決済

1回答

284閲覧

PHP配列で、複雑な条件に基づいて、指定の値を削除する方法

love_kinniky

総合スコア22

PHP

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

0グッド

0クリップ

投稿2019/02/06 05:19

「$update」を作りたいです。

###実現したいこと

下記の「$new」を使って、「$current」から値を削除して、「$update」を作りたいです。

しかしそのルールがかなり複雑で、しかも特にエラーも出ないためにダメな部分がわかりません。

文末に自分なりのコードをイメージで書いたので、ダメな部分が分かる方がいらっしゃいましたら正しいコードをご指南頂けませんでしょうか。

###「$update」を作るルール
問題のルールは次のようなものです。

「$current」の「'action'の値」に、「$new」の「'action'の値」と同じ値があるときに、「'id'の値」にも同じものがあれば、「$current」に以下➀➁が削除される

➀その同じ「'id'の値」を削除する。
➁削除した「'id'の値」と同じ位置にある「'time'の値」も削除する。

以下、配列とコードになります。
###「$new」
こちらが「$new」です。全ての値は常に1つずつです。

php

1$new = array( 2 '2019-01-02'=> array( 3 array( 4 'action'=>'greeting', 5 'id'=>array('7'), 6 'time'=>array('2019-01-03 07:00'), 7 ), 8 ), 9);

###「$current」
こちらが「$current」です。数は不明ですが、同じ日付に同じアクションは重複しません。
コメントアウトさせてあるような➀➁の削除を実現させたいと考えています。

php

1$current = array( 2 '2019-01-02'=> array( 3 array( 4 'action'=>'attack', 5 'id'=>array('3','7'), 6 'time'=>array('2019-01-02 21:00','2019-01-01 03:00'), 7 ), 8 ), 9 '2019-01-01'=> array( 10 array( 11 'action'=>'greeting', // 'action' の値が $new と同じなので、この配列に下記➀➁の変化を加える 12 'id'=>array('6','7'), // ➀ まず '7' を削除して、それが2番目であることを記憶しておく 13 'time'=>array('2019-01-01 15:00','2019-01-01 02:00'), // ➁ 2番目にある '2019-01-01 02:00' を削除する 14 ), 15 ), 16);

###試しているコード
こちらのコードが現在考えているものですが、エラーは出ず、しかし削除もされません。

php

1foreach($new as $day => $contents) { 2 foreach($contents as $content) { 3 for($i = 0; $i < count($current[$day]); $i++) { 4 // 'action' が同じ場合 5 if($current[$day][$i]['action'] !== $content['action']) { 6 // 'id' が同じ場合 7 if ( in_array($content['id'], $current[$day][$i]['id']) ) { 8 // 同じ 'id'を $current から削除 9 while( ($index = array_search( $content['id'], $current[$day][$i]['id'], true )) !== false ) { 10 unset( $array[$index] ) ; 11 } 12 // 削除した 'id' と同じ位置の'time'を削除 13 $position = array_search($content['id'], $current[$day][$i]['id']); // 削除した 'id' の位置を取得 14 unset($current[$day][$i]['time'][$position]); // その位置の 'time' を削除 15 } 16 } 17 } 18 } 19} 20$update = $current; 21var_export($update);

###「$update」
最後に実現したい「$update」を掲載しておきます。コメントアウトしてある2点が望む変化です。

php

1$current = array( 2 '2019-01-02'=> array( 3 array( 4 'action'=>'attack', 5 'id'=>array('3','7'), 6 'time'=>array('2019-01-02 21:00','2019-01-01 03:00'), 7 ), 8 ), 9 '2019-01-01'=> array( 10 array( 11 'action'=>'greeting', 12 'id'=>array('6'), // 削除されてる 13 'time'=>array('2019-01-01 15:00') //削除されてる 14 ), 15 ), 16);

以上です。長々と申し訳ございませんが、ご査収頂ければ幸いです。
何卒宜しくお願い致します。

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

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

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

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

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

m.ts10806

2019/02/06 05:34

そもそこの「ルール」とやらはどこからどのように出てきたものなのでしょうか。もし学校や何かの課題、なのであれば回答は得られ難いかと思います。
love_kinniky

2019/02/06 05:40

学生に思ってもらえるとは光栄です。苦笑
m.ts10806

2019/02/06 05:43

そのあたり書いてないと誰も何も分かりませんので。それに「学校」とは書きましたが本当の学生(~大学生)ではなくても「プログラミングスクールの課題」とか「オンライン学習の課題」とか「新卒教育の課題」とか成人以降でもありますからね。 ひとまず下記追記願います↓ そもそこの「ルール」とやらはどこからどのように出てきたものなのでしょうか。
guest

回答1

0

ベストアンサー

明確にされていなかった仕様が想定外のほうになったので、関数作りました。
御社の仕事をするとPHPに対する自信が崩壊していきます。

php

1var_export(trimmingCopy($new, $current)); 2function trimmingCopy($new, $current) { 3 $param = pickParam($new); 4 $days = array_keys($current); 5 $ret = []; 6 foreach($days as $day) { 7 foreach($current[$day] as $item) { 8 if($item['action'] !== $param['action']) { 9 $ret[$day][] = $item; 10 continue; 11 } 12 $index = array_search($param['id'], $item['id'], true); 13 if($index === false) { 14 $ret[$day][] = $item; 15 continue; 16 } 17 array_splice($item['id'], $index, 1); 18 array_splice($item['time'], $index, 1); 19 $ret[$day][] = $item; 20 } 21 } 22 return $ret; 23} 24function pickParam($new) { 25 ['action' => $act, 'id' => $id,/* 'time' => $time*/] = array_pop($new)[0]; 26 return ['action' => $act, 'id' => $id[0],/* 'time' => array_pop($time)*/]; 27}

deprecated
どっちにしても丸投げですが、添削してほしいのか、作って欲しいのかは明確にしてください。

今回添削でやりました。できる限り現状コードを生かしています。

php

1<?php 2foreach($new as $day => $contents) { 3 foreach($contents as $content) { 4 for($i = 0; $i < count($current[$day]); $i++) { 5 // 'action' が同じ場合 6 if($current[$day][$i]['action'] === $content['action']) { 7 // 'id' が同じ場合 8 if ( in_array($content['id'][0], $current[$day][$i]['id']) ) { 9 // 同じ 'id'を $current から削除 10 $index = array_search( $content['id'][0], $current[$day][$i]['id'], true ); 11 if($index !== false) { 12 unset( $current[$day][$i]['id'][$index] ) ; 13 // 削除した 'id' と同じ位置の'time'を削除 14 unset($current[$day][$i]['time'][$index]); // その位置の 'time' を削除 15 } 16 } 17 } 18 } 19 } 20} 21$update = $current; 22var_export($update); 23 24$current = array( 25 '2019-01-02'=> array( 26 array( 27 'action'=>'attack', 28 'id'=>array('3','7'), 29 'time'=>array('2019-01-02 21:00','2019-01-01 03:00'), 30 ), 31 ), 32 '2019-01-01'=> array( 33 array( 34 'action'=>'greeting', // 'action' の値が $new と同じなので、この配列に下記➀➁の変化を加える 35 'id'=>array('6','7'), // ➀ まず '7' を削除して、それが2番目であることを記憶しておく 36 'time'=>array('2019-01-01 15:00','2019-01-01 02:00'), // ➁ 2番目にある '2019-01-01 02:00' を削除する 37 ), 38 ), 39); 40$new = array( 41 '2019-01-02'=> array( 42 array( 43 'action'=>'greeting', 44 'id'=>array('7'), 45 'time'=>array('2019-01-03 07:00'), 46 ), 47 ), 48);

投稿2019/02/06 05:35

編集2019/02/06 06:59
papinianus

総合スコア12705

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

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

love_kinniky

2019/02/06 05:43

質問のコードと同じようですが、
papinianus

2019/02/06 05:45

今リロードしてますが、違ってますね(whileがないので明らかに違うはず)。 たまにキャッシュでおかしな見えかたをすることがあります。
love_kinniky

2019/02/06 05:47

申し訳ございません。途中で送信してしまったようです。「コードの内容が同じ」ではなく、「出力が同じ」という意味でした。
madoka9393

2019/02/06 05:51

ちゃんと実行してたら出力が同じだなんて言えるはずないんだよなぁ…。
papinianus

2019/02/06 05:51

今↓を if($current[$day][$i]['action'] !== $content['action']) { こう↓直しました if($current[$day][$i]['action'] === $content['action']) { すると、 「出力が同じ」という結果になりました。 ところで、日付の一致を判定しているので、今回の理想形は生まれ得ないんですが、日付は無視したほうがいいのですか?
love_kinniky

2019/02/06 06:07 編集

なるほど、失礼致しました。 あと、日付は無視したいです。「日付の一致の判定」は不要で、「日付に関係なく、「$current」の全てから、「'action'」が一致するかどうか」で、削除➀➁を行いたいです。 でも日付の一致の判定って、コードのどの部分でされていますか? 別のページからコピペして、今回実現したいことのためにアレンジしたので、コピペした部分についての理解がございません。。
papinianus

2019/02/06 06:08

$dayでループしているからです。判定っていうか、制約に近いコーディングですが。 > 「$new」の「'action'」は「'greeting'」なので、削除対象の配列は、『「$current」の「'attack'」が「'greeting'」の配列』にしたいところです。 この意図は汲んでいるつもりでした。まあ日付の件もあるので再考します。
love_kinniky

2019/02/06 06:13

$dayでループ、あ、そこですね。完全に理解せずコピペしておりました… >この意図は汲んでいるつもりでした。 はい、失礼致しました。日付さえ同じにすれば意図したとおりきちんと動きました↓ http://codepad.org/uQQfd7Xx あとは日付の制約とやらですね。自分なりに$dayのあたりをいじって模索してみます。
papinianus

2019/02/06 06:52 編集

$daysをいじっちゃうとなんかいろいろつらい気がしたんで、作りかえました。
love_kinniky

2019/02/06 06:54

こんにちは。お世話になっております。 あれから考えてこのようにしてみたのですが、 http://codepad.org/LEfRvr4A 近いでしょうか? まず $new の 'action' と 'id' を、それぞれ「$newaction」と「$newid」として取得しておいて、$current のループと削除は「$newaction」と「$newid」に応じてやる。 というイメージで書いてみたつもりです。
papinianus

2019/02/06 06:57 編集

そうですね。添削方針でいくとするとこうなると思います。関数化してコピーしてますが、判定ロジックの本質は同じだと思います。 逆に言うと、関数化したのにつらみが消えてないってことですね。
papinianus

2019/02/06 07:01

codepadのやつで、44行目のifは不要ですね。41行目でin_arrayをしているのでfalseにならないことが確定している(厳密にはarray_searchだけtrueになっているので型違いでfalseになる可能性はある)
love_kinniky

2019/02/06 09:18 編集

入れ違いで、 15:52 のご返信とご回答の関数を今拝見致しました。 理想的です!どうもありがとうございます。 今まで何度も日付ベースでやっていたのに今回の質問でいきなり日付無視ですもんね。「仕様が想定外」と仰るのも当然で、その仕様を事前に書かずにお手間をとらせてしまったこと、改めてお詫び申し上げます。 また、入れ違いコメント(15:54)の自作のcodepadの方についても、ご指摘ありがとうございます。 仰るようにifは消したりして、なんとかできました。(http://codepad.org/pFP8TwvA)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問