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

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

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

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

Q&A

解決済

2回答

980閲覧

PHPの複雑な配列の連結について教えて頂けませんでしょうか

love_kinniky

総合スコア22

PHP

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

0グッド

0クリップ

投稿2019/02/01 08:32

実現したいこと

下記の「新しい配列 $new」と「現在の配列 $current」を、「目的の配列 $update」にすることが目的です。

文末のソースコードまで漕ぎつけたのですが配列操作が初心者すぎて歯が立たず、質問させて頂きました。

どなたか適切な書き方を教えて頂けませんでしょうか。

###詳しい操作
その配列操作としては、まず

$newと日付が同じ$currentの中から、'action'=>'apple'の配列を合体し、一番上に移動する。

という操作があり、そしてこの合体にあたって、

'time'を連結する。(20件まで)
'actorID'を連結する。(20件まで)
'allow''undone'にする。

という計4つの操作があります。

「新しい配列 $new」と「現在の配列 $current」

下記2つの配列が操作対象です。

/* * 新しい配列 $new */ $new = array( '2019-01-02'=> array( array( 'action'=>'apple', 'time'=>'2019-01-02 17:00:00.000', 'targetID'=>'1', 'actorID'=>'17', 'allow'=>'undone', ) ) ); /* * 現在の配列 $current */ $current = array( '2019-01-02'=> array( array( 'action'=>'orange', 'time'=>array('2019-01-02 16:00:00.000','2019-01-01 15:00:00.000'), 'targetID'=>'200', 'authorID'=>'1', 'actorID'=>array('16','15'), 'allow'=>'undone', ), array( 'action'=>'apple', 'time'=>array('2019-01-02 14:00:00.000','2019-01-01 13:00:00.000'), 'targetID'=>'1', 'actorID'=>array('14','13'), 'allow'=>'done', ), ), '2019-01-01'=> array( array( 'action'=>'apple', 'time'=>array('2019-01-01 04:00:00.000','2019-01-01 03:00:00.000'), 'targetID'=>'1', 'actorID'=>array('4','3'), 'allow'=>'done', ), ), );

目的の配列

上の2つの配列について、➀➁➂➃の操作によって作りたい配列が下記になります。

/* * 目的の配列 $update */ $update = array( '2019-01-02'=> array( array( // 日付が同じappleなので➀➁➂➃をする 'action'=>'apple', 'time'=>array('2019-01-02 17:00:00.000,2019-01-02 14:00:00.000','2019-01-01 13:00:00.000'), 'targetID'=>'1', 'actorID'=>array('17','14','13'), 'allow'=>'undone', ), array( // 日付が同じだけどappleではないので変化なし 'action'=>'orange', 'time'=>array('2019-01-02 16:00:00.000','2019-01-01 15:00:00.000'), 'targetID'=>'200', 'authorID'=>'1', 'actorID'=>array('16','15'), 'allow'=>'undone', ), ), '2019-01-01'=> array( array( // 日付が違うので変化なし 'action'=>'apple', 'time'=>array('2019-01-01 04:00:00.000','2019-01-01 03:00:00.000'), 'targetID'=>'1', 'actorID'=>array('4','3'), 'allow'=>'done', ), ), );

試したこと

➀➁➂➃の操作のために書いたソースコードがこちらです。正直自信がまったくないのですが、せめてイメージが伝わればと思い、お恥ずかしながら掲載させて頂きます。

php

1/* 2* 目的の配列を作る 3* usage: $update = mymerge($new, $current); 4*/ 5function mymerge($arg1, $arg2) { 6 foreach ($arg1 as $item) { 7 $newdate = $item[0]; // 新しい配列の日付を取得 8 // appleの連結 9 if ($item['action'] === 'apple') { 10 foreach ($arg2 as $key=>$val) { 11 // appleで日付が同じなら連結 12 if ($val['action'] === 'apple' && $val[0] === $newdate ) { 13 array_unshift($arg2[$key]['time'], $item['time']);// timeを連結 14 array_unshift($arg2[$key]['actorID'], $item['actorID']);// actorIDを連結 15 $arg2[$key]['allow'] = 'undone'; // 未許可にする 16 while (count($arg2[$key]['actorID']) > 20) { array_pop($arg2[$key]['actorID']); } // 20件以降は追加しない 17 $add_apple = true; 18 break; 19 } 20 } 21 if ($add_apple) { continue; } 22 } 23 array_unshift($arg2, $item); 24 } 25 return($arg2); 26} 27$update = mymerge( $add, $current );

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2019/02/01 08:39

合体させる、の意味が曖昧だと思います。キーが重複したら両方残すのか、一方のみ残すのかを決めてほしい。
love_kinniky

2019/02/01 08:43

失礼致しました。'apple' のみの合体なので、キー( 'action' と 'time' と 'targetID' と 'actorID' と 'allow' )は必ず重複することになると思います。わかりにくい点がございましたらまたご指摘ください。
ssasaki

2019/02/01 08:48

$new の方は、実体としては 2019-01-02 の action=apple なデータしかありませんが、実際には他のデータもたくさんあったりするのでしょうか? それとも、掲載されている通り、フォーマットは配列の形になっているものの action=apple なデータ1つしかないのは確定しているのでしょうか?
love_kinniky

2019/02/01 08:55 編集

追加される$newは常に1つです。 しかしそのパターンとしては「action=apple」以外にも「action=orange」など複数ありえます。 つまり$newのパターンとしては、「action=apple」が1つか、「action=oragne」が1つか、「action=banana」が1つか、などになります。 蛇足ですが、まずは今回のappleのパターンでご回答頂ければ、それ以外のorangeのパターンなどは自分でできるのではないかと考え、appleのパターンだけについて質問させて頂きました。
guest

回答2

0

他にも条件ありそうですが、ざっとコード書いてみましたので参考にどうぞ。
一応目的の配列にはなっているかと思います。

PHP

1function mymerge($arg1, $arg2, $target) { 2 $newdate = key($arg1); 3 $newarr = $arg1[$newdate][0]; 4 if ($newarr['action'] !== $target) return $arg2; 5 6 foreach ($arg2 as $date => $arr) { 7 if ($date == $newdate) { 8 foreach ($arr as $n => $tarr) { 9 if ($tarr['action'] === $target) { 10 array_unshift($tarr['time'], $newarr['time']); 11 array_unshift($tarr['actorID'], $newarr['actorID']); 12 $tarr['allow'] = $newarr['allow']; 13 array_splice($arg2[$date], $n, 1); 14 array_unshift($arg2[$date], $tarr); 15 break; 16 } 17 } 18 } 19 } 20 return $arg2; 21} 22 23$update = mymerge( $new, $current, 'apple'); 24 25print_r($update);

投稿2019/02/01 09:27

ssasaki

総合スコア1167

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

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

love_kinniky

2019/02/01 10:32

どうもありがとうございます。問題なく作動しているように思います。 papinianus様のご回答と照らし合わせつつ、また自分の条件を再考しつつ、これから取り組んでみたいと思います。
guest

0

ベストアンサー

独自解釈につきご容赦ください

  • 20個まで付くやつ降順ソートで20個ではないのか?
  • newのやつが上にくるとのことだが、actionだけでみたとき、$current側に、appleが複数いる可能性が否定されていない。またallowをundoneにするという意図からしても、newがついたやつが上にくるのではなく、undoneがうえからならんでいればいいのではないかと考え、そうコード化した。

(doneよりも下にundoneがいることは対処が必要だと思ったが、今回の例の1/2においてcurrentでundoneのorangeよりappleが上にくることに実用上の必然性を見出せなかった)

php

1<?php 2/* 3* 新しい配列 $new 4*/ 5$new = array( 6 '2019-01-02'=> array( 7 array( 8 'action'=>'apple', 9 'time'=>'2019-01-02 17:00:00.000', 10 'targetID'=>'1', 11 'actorID'=>'17', 12 'allow'=>'undone', 13 ) 14 ) 15); 16 17/* 18* 現在の配列 $current 19*/ 20$current = array( 21 '2019-01-02'=> array( 22 array( 23 'action'=>'orange', 24 'time'=>array('2019-01-02 16:00:00.000','2019-01-01 15:00:00.000'), 25 'targetID'=>'200', 26 'authorID'=>'1', 27 'actorID'=>array('16','15'), 28 'allow'=>'undone', 29 ), 30 array( 31 'action'=>'apple', 32 'time'=>array('2019-01-02 14:00:00.000','2019-01-01 13:00:00.000'), 33 'targetID'=>'1', 34 'actorID'=>array('14','13'), 35 'allow'=>'done', 36 ), 37 ), 38 '2019-01-01'=> array( 39 array( 40 'action'=>'apple', 41 'time'=>array('2019-01-01 04:00:00.000','2019-01-01 03:00:00.000'), 42 'targetID'=>'1', 43 'actorID'=>array('4','3'), 44 'allow'=>'done', 45 ), 46 ), 47 48); 49 50foreach($new as $day => $contents) { 51 foreach($contents as $content) { 52 if(!isset($current[$day])) { continue; } //その日がない、スキップ 53 for($i = 0; $i < count($current[$day]); $i++) { 54 if($current[$day][$i]['action'] !== $content['action']) {continue;} //action違い、スキップ 55 //$current[$day][$i]['time'] = array_slice(array_merge($current[$day][$i]['time'],[$content['time']]), 0, 20); 56 //rsort($current[$day][$i]['time']); 57 $current[$day][$i]['time'] = array_slice(array_merge([$content['time']], $current[$day][$i]['time']), 0, 20); 58 //$current[$day][$i]['actorID'] = array_slice(array_merge($current[$day][$i]['actorID'],[$content['actorID']]), 0, 4); 59 //rsort($current[$day][$i]['actorID']); 60 $current[$day][$i]['actorID'] = array_slice(array_merge([$content['actorID']], $current[$day][$i]['actorID']), 0, 4); 61 $current[$day][$i]['allow'] = 'undone'; 62 } 63 $sortkey = array_map(function($ar) { return $ar[0];}, array_column($current[$day], 'time')); 64 array_multisort($sortkey, SORT_DESC,$current[$day]); 65 } 66} 67var_export($current);

投稿2019/02/01 09:03

編集2019/02/01 12:56
papinianus

総合スコア12705

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

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

papinianus

2019/02/01 09:24 編集

「newのやつが上にくる」の補足。できあがったデータから上にくるやつを特定したいということです。 フラグ(last_updateカラムなど)をもつならそれでソートしましょう。 しかし今のデータでは1/2のappleをorangeより上にするデータ的根拠がない。orangeも何らかの事情でundoneになったはずで、appleが後にきたのは、何らかの順序でしかなく、その順序に意味を感じるのは、申し訳ないが幻想です。 もし、それに基づいて処理すべきなら、undoneをcurrentにもつのではなく、doneとundoneに分かれたデータ構造としたい。
love_kinniky

2019/02/01 09:55

どうもありがとうございます。その日がないときにスキップさせるなど、重要で失念していた機能をつけて頂き感謝です! さてご指摘の点について以下4つご返信させて頂きます。 ----- 1 >20個まで付くやつ降順ソートで20個ではないのか? →新しいのがどんどん左に入る感じです。 ----- 2 >$current側に、appleが複数いる可能性が否定されていない。 →$current側にappleが複数いるとしたら、別の日付で入るはずです。同じ日付なら全て合体されて1つになるためです。 ----- 3 >allowをundoneにするという意図からしても、newがついたやつが上にくるのではなく、undoneがうえからならんでいればいいのではないか →ご配慮に感謝です。ただ未許可(undone)上に来るのではなくて、新しい日付が上に来るのが望ましいと考えています。 ----- 4 >今のデータでは1/2のappleをorangeより上にするデータ的根拠がない。 >その順序に意味を感じるのは、申し訳ないが幻想です。 →doneとundoneは並び順に関与しない要素として扱いたいと考えています。あくまで並び順は、「新しい'time'を持つapple(またはorangeやbananaなど)が入っているarray」になります。
papinianus

2019/02/01 10:05

たぶん質問者様だけの力ではこのデータ構造を見直せないんだと思う。それ分かったうえでいいます 1. 新しいのが左 →これが、降順の意味です。連結・合体で当然に左にくると思うのは無理です(左ではなく配列の先頭だと解釈してですが)。actorIDは?大きいほうからでは? 2. $current側にappleが複数いるとしたら、別の日付で入るはず →それは全くコードでもデータでも保証されていない。「はず」にすぎない。もしそのことが保証されているなら、全く違うコードを作らなければならない。もし保証されているとして改訂するとしたら、1/2のappleがない可能性はどうするという話になります。 3. 未許可(undone)上に来るのではなくて →undoneがソートのキーとなるようなものでないことは承知のうえです。ただ、ご自身の発言にあるように「新しい日付が上に来るのが望ましい」ですよね。それは理解しました。つまり配列に'allow'=>'undone'のあとにでも、'last_updated' => $newのtimeというものがあるべきで、かつそれによってソート可能であるデータ構造が望ましいのです(これがフラグで書いた意味) 4. 3とからみますが、last_updatedをつけずこの状態で、timeでソートする方法は考えてみます。
papinianus

2019/02/01 10:16

timeの0番目の値で比較して上位にくるように修正しました。 このコードでnewを足すときは、0が最新になるよう手当していますが、$currentのtimeがもともとそうなっているかどうかは、このコードでは確認していません。 foreachの部分を取り出せば関数化することは容易でしょう。 またappleには依存していません。
love_kinniky

2019/02/01 10:29

ありがとうございます。仰るとおり、構造の見直しができるのは7割といったところです。。先行きが不安ですね。 ----- 1 なるほど。ではまさに降順で、配列の先頭です。actorIDはtimeと同じ並び順を考えています。 ----- 2 「はず」はコードで保証されませんでしょうか?質問の操作➀が達成されることによって、強制的に$currentの同日には1つのappleしかありえず、「複数のappleがあれば別の日になっているはず」だと思うのですが。 ----- 3、4 なるほど!'last_updated' をつくってよってソート可能にするとは、思いつきませんでした。あれば便利ですね。それでしたら自分で追加できるかもしれません。考えてみます。
love_kinniky

2019/02/01 10:30

すみません。19:16 を今拝見いたしました。ご修正どうもありがとうございます。これからじっくり見てみたいと思います。
papinianus

2019/02/01 10:35

last_updatedなしで動きます。 actorIDがtimeと同じ、の同じの意味が多分降順ではないので、あとで直します
love_kinniky

2019/02/01 10:51

言葉選びの浅はかさに引きます…wたしかに、「同じ」が意味するのは「timeの降順と同じように、actorIDも降順にする」ではなくて、「timeの降順と同じ並びに、actorIDを合わせる」でした。重ね重ねご配慮に感謝致します。
papinianus

2019/02/01 13:01

作るものの要件として、データが処理される順序に意味を持たせることが、どうしても必要なようなので、rsort()によって、データの意味によって並べるのではなく、来たものをより先頭に回す。という措置にかえました。 比較のために残していますが、array_mergeによって結合する順序をかえただけです。
love_kinniky

2019/02/01 18:10

こんばんは。お世話になっております。 来たものをより先頭に回す、ベストな処理です。なるほど、array_mergeでは前後の並びにできるんですね。お蔭さまで理想的な処理になりました。 たびたびのご返信、心より感謝申し上げます。どうもありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問