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

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

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

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

Q&A

解決済

2回答

564閲覧

PHPで配列を比較し、特定のキーに同じ値があれば、別のキーを更新したい。

natsunoomoide

総合スコア8

PHP

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

0グッド

0クリップ

投稿2020/02/25 03:04

編集2020/02/25 03:57

###実現したいこと
1.$arr$newを比較し、valが同じ値ならdateを更新し、同じ値でなければ追加したい。
2.$arrは最大4件までとしたい。

宜しくお願い致します。

###該当のソースコード
まず元からある配列として$arrがあります。

PHP

1$arr = [ 2 ['val'=>'a1','date'=>'2020-02-25 01:00:00'], 3 ['val'=>'a2','date'=>'2020-02-25 02:00:00'], 4 ['val'=>'a3','date'=>'2020-02-25 03:00:00'] 5];

次の$newが与えられた場合、valと同じ値が$arrにあるので、そのdateを更新して$result1を得るのが目的です。

PHP

1$new = ['val'=>'a2','date'=>'2020-02-25 04:00:00']; 2 3$result1 = [ 4 ['val'=>'a1','date'=>'2020-02-25 01:00:00'], 5 ['val'=>'a2','date'=>'2020-02-25 04:00:00'], // valが同じ値なのでdateを更新 6 ['val'=>'a3','date'=>'2020-02-25 03:00:00'] 7];

もし$newが次の場合、$arrに同じ値がないので、普通に追加して以下$result2を得たいです。

PHP

1$new = ['val'=>'a4','date'=>'2020-02-25 04:00:00']; 2 3$result2 = [ 4 ['val'=>'a1','date'=>'2020-02-25 01:00:00'], 5 ['val'=>'a2','date'=>'2020-02-25 02:00:00'], 6 ['val'=>'a3','date'=>'2020-02-25 03:00:00'], 7 ['val'=>'a4','date'=>'2020-02-25 04:00:00'] // 普通に追加 8];

ここまでは1の処理です。もしできれば2の処理として最新の4件までとし、古いものは削除したいのですが、これは文末に「2のロジック」として追記させて頂きました。(あちこち飛んで申し訳ございません。)

###発生している問題(1について)
まず1についてです。

1.$arr$newを比較し、valが同じ値ならdateを更新したい。

foreachで試したのですが、これでは配列が増えすぎてしまいました。
原因はifの分岐で、同じ値があってもなくても該当して追加されてしまうからでしょうか?

PHP

1// 同じ値があればその日付を更新し、なければ追加 2foreach ( $arr as &$ar ) { 3 if ( $ar['val'] == $new['val'] ) { 4 $ar['date'] = $new['date']; 5 } else { 6 $arr[] = $new; 7 } 8} 9var_dump($arr);

###試したこと(1について)
上のifがおかしいなら、更新と追加の処理を次のようにして分けたらどうかと思いましたが、これでもやはり余計な配列が増えてしまいます。

PHP

1// $newのvalと同じ値があればその日付を更新し、 2foreach ( $arr as &$ar ) { 3 if ( $ar['val'] == $new['val'] ) { 4 $ar['date'] = $new['date']; break; 5 } 6} 7// なければ追加 8foreach ( $arr as $ar ) { 9 if ( $ar['val'] != $new['val'] ) { 10 $arr[] = $new; break; 11 } 12} 13var_dump($arr);

###発生している問題(2について)
そして2についてです

2.$arrは最大4件までとしたい。

これは次のコードしかわからずコメントアウトしてあるようなエラーになります。

array_keysに渡せるのは配列だけ」というエラーかと思うのですが、でも最大値を見たいのはdateだけなので$arr['date']とする以外にどうしたらいいのかわからず、とりあえずエラーのままのコードですみませんが掲載させて頂きました。

PHP

1// 4件以上なら古いdateのものを削除 2if ( count($arr) >= 4 ) { 3 $maxes = array_keys($arr['date'], max($arr['date'])); 4 $max = $maxes[0]; 5 unset($arr[$max]); 6} 7var_dump($arr); 8 9/* 10エラーメッセージ 11PHP Notice: Undefined index: date in /aaa/bbb.php on line 0 12PHP Notice: Undefined index: date in /aaa/bbb.php on line 0 13PHP Warning: max(): When only one parameter is given, it must be an array in /aaa/bbb.php on line 0 14PHP Warning: array_keys() expects parameter 1 to be array, null given in /aaa/bbb.php on line 0 15*/

###試したこと(2について)
すみません、最大値を得る方法が上の書き方しかわからず、他に試すことが思い浮かびませんでした。

###バージョン
PHP 7.2.13 です。

###2のロジック
2のロジックについてご説明させて頂きます。

以下の場合$newval$arrのどれとも一致しないので、1を介して$arrに普通に追加されることになり$result2がまず得られます。

しかしその結果5件になるので、その5件から古いものを削除し$result3を得たいということを考えています。これが2の処理です。

(自分がわかりやすいので1と2の処理に分けていますが、欲しいのは$result3だけで、その過程にある$result1$result2の方は不要です。)

PHP

1$arr = [ 2 ['val'=>'a1','date'=>'2020-02-25 01:00:00'], 3 ['val'=>'a2','date'=>'2020-02-25 02:00:00'], 4 ['val'=>'a3','date'=>'2020-02-25 03:00:00'], 5 ['val'=>'a4','date'=>'2020-02-25 04:00:00'] 6]; 7 8$new = ['val'=>'a5','date'=>'2020-02-25 05:00:00']; 9 10// ↓ 1の処理で$result2が得られる 11 12$result2 = [ 13 ['val'=>'a1','date'=>'2020-02-25 01:00:00'], 14 ['val'=>'a2','date'=>'2020-02-25 02:00:00'], 15 ['val'=>'a3','date'=>'2020-02-25 03:00:00'], 16 ['val'=>'a4','date'=>'2020-02-25 04:00:00'], 17 ['val'=>'a5','date'=>'2020-02-25 05:00:00'] 18]; 19 20// ↓ 2の処理で古いものを削除し以下$result3を得たい 21 22$result3 = [ 23 ['val'=>'a2','date'=>'2020-02-25 02:00:00'], 24 ['val'=>'a3','date'=>'2020-02-25 03:00:00'], 25 ['val'=>'a4','date'=>'2020-02-25 04:00:00'], 26 ['val'=>'a5','date'=>'2020-02-25 05:00:00'] 27]; 28

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

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

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

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

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

guest

回答2

0

ベストアンサー

1.$arrと$newを比較し、valが同じ値ならdateを更新したい。

冗長な感じはありますが、愚直にやるならこうかなあ。
ポイントとしては$arrを直接いじりたいなら$arr[$key]['date']みたいにする必要がある点です。(これを$row['date'] = $newRow['date']としても$arrは書き換わりません。)

PHP

1 2 3$arr = [ 4 ['val'=>'a1','date'=>'2020-02-25 01:00:00'], 5 ['val'=>'a2','date'=>'2020-02-25 02:00:00'], 6 ['val'=>'a3','date'=>'2020-02-25 03:00:00'] 7]; 8 9$new = ['val'=>'a4','date'=>'2020-02-25 04:00:00']; 10 11$isInArray = false; 12foreach($arr as $key => $row ){ 13 if($row['val'] === $new['val']){ 14 $arr[$key]['date'] = $new['date']; 15 $isInArray = true; 16 } 17} 18 19if($isInArray === false){ 20 $arr[] = $new; 21} 22 23var_dump($arr); 24 25 26/* 27array(4) { 28 [0] => 29 array(2) { 30 'val' => 31 string(2) "a1" 32 'date' => 33 string(19) "2020-02-25 01:00:00" 34 } 35 [1] => 36 array(2) { 37 'val' => 38 string(2) "a2" 39 'date' => 40 string(19) "2020-02-25 02:00:00" 41 } 42 [2] => 43 array(2) { 44 'val' => 45 string(2) "a3" 46 'date' => 47 string(19) "2020-02-25 03:00:00" 48 } 49 [3] => 50 array(2) { 51 'val' => 52 string(2) "a4" 53 'date' => 54 string(19) "2020-02-25 04:00:00" 55 } 56} 57*/ 58

2.$arrは最大4件までとしたい。

に関しては、
PHPマニュアル array_multisort
PHPの多次元連想配列のソート

を参考にソートして、ソート後の配列を必要な数だけ取り出す という感じで実装できるかと思います。

データの持ち方を変えればもっと楽な例

PHP

1<?php 2 3//元のデータがこんな感じであるとして 4$arr = [ 5 ['val'=>'a1','date'=>'2020-02-25 01:00:00'], 6 ['val'=>'a2','date'=>'2020-02-25 02:00:00'], 7 ['val'=>'a3','date'=>'2020-02-25 03:00:00'] 8]; 9 10foreach($arr as $row){ 11 $newArr[$row['val']] = $row; 12} 13 14$new = ['val'=>'a4','date'=>'2020-02-25 04:00:00']; 15 16//データが無いときは追加であるときは更新 17$newArr[$new['val']] = $new; 18 19 20var_dump($newArr);

投稿2020/02/25 03:18

編集2020/02/25 04:24
tanat

総合スコア18713

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

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

natsunoomoide

2020/02/25 03:30

>$arr[$key]['date']みたいにする必要がある点です なるほど、&をつければいいのかと思っていました。ありがとうございます。
natsunoomoide

2020/02/25 03:31

if($row['val'] === $newRow['val']) の行で、以下のエラーとなりました。 Illegal string offset 'val' in /aaa/bbb.php on line 0
natsunoomoide

2020/02/25 03:31

2に関して参考リンクありがとうございます。見てきます。
tanat

2020/02/25 03:39

> if($row['val'] === $newRow['val']) > の行で、以下のエラーとなりました。 $newのデータ構造を誤解していたので修正しました。(複数の書き換えをするのかと思いこんでいました)
tanat

2020/02/25 03:43

> なるほど、&をつければいいのかと思っていました。ありがとうございます。 参照渡しをしても可能です。が、PHPはブロックスコープが無いので後に影響が出ることがある点を注意する必要があります。
natsunoomoide

2020/02/25 03:46 編集

ご修正ありがとうございます。ところで「なければ追加」の方はいかがでしょうか。 「発生している問題(1について)」や「試したこと(1について)」にあるのと同じ流れで以下elseを追加してみたのですが、やはり同じように余分な配列が追加されてしまう問題が発生してしまいました。 以下$newではvalがa4となっていて、これは$arrにないので「なければ追加」をしたいのですが、var_dumpされる配列は$arrの3つに$newの1つが追加された計4つでなく、6つになってしまうという問題です。 $new = ['val'=>'a4','date'=>'2020-02-25 04:00:00']; foreach($arr as $key => $row ){ if($row['val'] === $new['val']){ $arr[$key]['date'] = $new['date']; }else{ $arr[] = $new; // ここで「なければ追加」としたつもりです } } var_dump($arr);
tanat

2020/02/25 03:46

あ、ソース中のコメント // 同じ値があればその日付を更新し、なければ追加 は見ていませんでした。(これは実現したいこと のところに追記をして頂きたいです。)
tanat

2020/02/25 03:55

「無ければ追記」に対応しました。 これはforeachの中でやっちゃうとループ回数分増えちゃうので、foreachの外で実施するか、最初の一回だけ実施する必要がありますね。
natsunoomoide

2020/02/25 04:00

コメントアウトにしか書いてなくてすみませんでした。実現したいことに追記しました。 $isInArrayなるほどです!質問でわざわざ2回ループしてるの馬鹿すぎましたね。ありがとうございます! 今2の処理はリンク先を参考にしなばら実装中なのでできる限り頑張ってみます。万が一わからないことがございましたらまた宜しくお願い致します。
natsunoomoide

2020/02/25 04:20

こちらで出来たのではないかと!ご助力頂きましてありがとうございました。 何かおかしいところなどないでしょうか。 少し思ったのは最後の「2.最新の4つのみとし、古いのは削除」の処理ですが、下記のようにソートしてから削除という流れではなくて、一発で古いのを見つけて削除のような処理はないものでしょうか。 <?php /* 前提 -----------------------------------------------*/ // 元からある配列 $arr = [ ['val'=>'a1','date'=>'2020-02-25 01:00:00'], ['val'=>'a2','date'=>'2020-02-25 02:00:00'], ['val'=>'a3','date'=>'2020-02-25 03:00:00'], ['val'=>'a4','date'=>'2020-02-25 04:00:00'] ]; // 新しい配列 $new = ['val'=>'a2','date'=>'2020-02-25 05:00:00']; // dateを更新する場合はこれ // $new = ['val'=>'a5','date'=>'2020-02-25 05:00:00']; // 普通に追加する場合はこれ /* 1.valが同じならdateを更新、なければ追加 -----------------------------------------------*/ $isInArray = false; // dateを更新 foreach($arr as $key => $row ){ if($row['val'] === $new['val']){ $arr[$key]['date'] = $new['date']; $isInArray = true; } } // なければ追加 if($isInArray === false){ $arr[] = $new; } //var_dump($arr); /* 2.最新の4つのみとし、古いのは削除 -----------------------------------------------*/ // dateの値でソート foreach ( $arr as $key => $value) { $sort[$key] = $value['date']; } array_multisort($sort, SORT_ASC, $arr); //var_dump($arr); // $arrがこの時点でソートされている // 一番上のものを削除して4件にする if( count($arr) > 4 ){ unset($arr[0]); $arr = array_values($arr); // キーを振りなおす } var_dump($arr);
natsunoomoide

2020/02/25 04:23

あと、 unset($arr[0]); は $arr = unset($arr[0]); と”すると”ダメなのに、 array_values($arr); は $arr = array_values($arr); と”しないと”ダメ というの違いってなぜでしょうね。かなりわかりにくくないですか?
tanat

2020/02/25 04:26

参考までにデータの持ち方を変えればそもそももっと楽というケースを追記しました。
tanat

2020/02/25 04:27

> 何かおかしいところなどないでしょうか。 ぱっと見たところ大丈夫だと思いますが、コードレビューはteratailの範囲外だと思いますので、ご自身でテストケースを作って仕様を満たしているか確認してみてください。
tanat

2020/02/25 04:30

> というの違いってなぜでしょうね。かなりわかりにくくないですか? それぞれの関数の仕様がそうだから。としか言えないです。(PHPの関数の仕様は歴史的な経緯もあって統一性が無くわかりにくいところが多々あります。) 関数を使用する場合は必ずPHPマニュアルの該当ページを確認するように習慣づけて誤解を回避するしかないんじゃないかなと思います。
natsunoomoide

2020/02/25 04:31

>データの持ち方を変えれば 震えましたwどうもありがとうございました。
natsunoomoide

2020/02/25 04:32

>ご自身でテストケースを作って ですね。失礼致しました。色々試してみます。
tanat

2020/02/25 04:33

> 下記のようにソートしてから削除という流れではなくて、一発で古いのを見つけて削除のような処理はないものでしょうか。 最小値を自力で調べて、その際の配列のキーを記憶しておく、最後にそのキーの要素をunsetする とすれば出来るかと思いますが、ソートしてから削除の方があらゆる面で便利だと思います。
natsunoomoide

2020/02/25 04:33

>PHPマニュアルの該当ページを確認するように習慣づけて 統一性がないものって他にもあるんですね、ご忠告感謝です。注意します。
natsunoomoide

2020/02/25 04:34

>ソートしてから削除の方があらゆる面で便利だと思います なるほど、わかりやすいですしね。細かい質問にまで本当にありがとうございました。
guest

0

php

1$result=array_map(function($x) use($new){ 2 return $x["val"]===$new["val"]?$new:$x; 3},$arr); 4print_r($result);

投稿2020/02/25 03:13

yambejp

総合スコア114983

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

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

yambejp

2020/02/25 03:18

> 最大4件までとしたい 5件以上のデータが入ったサンプルと、どういうロジックで どれを残すのか例示ください
natsunoomoide

2020/02/25 03:46 編集

1の方、ありがとうございます。foreachを使わずに配列が操作できるとは感激でした。ありがとうございます。 実は1には「なければ追加」というのも考えていまして、「2のロジック」の方の$result2をその例とさせて頂きました。 そして2の方失礼致しました。「2のロジック」を追記させて頂きましたのでご査収いただけましたら幸いです。
yambejp

2020/02/25 03:50

最大4つまでとりだすならこう $max=4; $result=array_slice($result,count($result)-$max,$max);
natsunoomoide

2020/02/25 04:24

最大4つの処理、ありがとうございます。今回は日付の古いものを削除ということをしたかったので、まずソートが必要みたいでした。
yambejp

2020/02/25 04:27

私の処理は配列の後ろのほうから4件というのやり方です > 日付の古いもの となるとソートしてからでしょうね
natsunoomoide

2020/02/25 04:36

なるほど、高レベルな書き方をありがとうございます。今回はわかりやすさ重視でforeachを素直に使いたいと思いますが、大変勉強になりました。また宜しくお願い致します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問