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

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

ただいまの
回答率

88.09%

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

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 586

score 22

実現したいこと

下記の「新しい配列 $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',
        ),    
    ),    

);

試したこと

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

/* 
* 目的の配列を作る
* usage: $update = mymerge($new, $current); 
*/
function mymerge($arg1, $arg2) { 
    foreach ($arg1 as $item) {
        $newdate = $item[0]; // 新しい配列の日付を取得
        // appleの連結
        if ($item['action'] === 'apple') {
            foreach ($arg2 as $key=>$val) {
            // appleで日付が同じなら連結
            if ($val['action'] === 'apple' && $val[0] === $newdate ) {    
                    array_unshift($arg2[$key]['time'], $item['time']);// timeを連結
                    array_unshift($arg2[$key]['actorID'], $item['actorID']);// actorIDを連結
                    $arg2[$key]['allow'] = 'undone'; // 未許可にする
                    while (count($arg2[$key]['actorID']) > 20) { array_pop($arg2[$key]['actorID']); } // 20件以降は追加しない
                    $add_apple = true;
                    break;
                }
            }    
            if ($add_apple) { continue; }
        }
        array_unshift($arg2, $item);
    }
    return($arg2);
}
$update = mymerge( $add, $current );
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • m6u

    2019/02/01 17:39

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

    キャンセル

  • love_kinniky

    2019/02/01 17:43

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

    キャンセル

  • ssasaki

    2019/02/01 17:48

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

    キャンセル

  • love_kinniky

    2019/02/01 17:52 編集

    追加される$newは常に1つです。
    しかしそのパターンとしては「action=apple」以外にも「action=orange」など複数ありえます。

    つまり$newのパターンとしては、「action=apple」が1つか、「action=oragne」が1つか、「action=banana」が1つか、などになります。

    蛇足ですが、まずは今回のappleのパターンでご回答頂ければ、それ以外のorangeのパターンなどは自分でできるのではないかと考え、appleのパターンだけについて質問させて頂きました。

    キャンセル

回答 2

checkベストアンサー

+1

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

  • 20個まで付くやつ降順ソートで20個ではないのか?
  • newのやつが上にくるとのことだが、actionだけでみたとき、$current側に、appleが複数いる可能性が否定されていない。またallowをundoneにするという意図からしても、newがついたやつが上にくるのではなく、undoneがうえからならんでいればいいのではないかと考え、そうコード化した。
    (doneよりも下にundoneがいることは対処が必要だと思ったが、今回の例の1/2においてcurrentでundoneのorangeよりappleが上にくることに実用上の必然性を見出せなかった)
<?php
/* 
* 新しい配列 $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',
        ),    
    ),    

);

foreach($new as $day => $contents) {
    foreach($contents as $content) {
        if(!isset($current[$day])) { continue; } //その日がない、スキップ
        for($i = 0; $i < count($current[$day]); $i++) {
            if($current[$day][$i]['action'] !== $content['action']) {continue;} //action違い、スキップ
            //$current[$day][$i]['time'] = array_slice(array_merge($current[$day][$i]['time'],[$content['time']]), 0, 20);
            //rsort($current[$day][$i]['time']);
            $current[$day][$i]['time'] = array_slice(array_merge([$content['time']], $current[$day][$i]['time']), 0, 20);
            //$current[$day][$i]['actorID'] = array_slice(array_merge($current[$day][$i]['actorID'],[$content['actorID']]), 0, 4);
            //rsort($current[$day][$i]['actorID']);
            $current[$day][$i]['actorID'] = array_slice(array_merge([$content['actorID']], $current[$day][$i]['actorID']), 0, 4);
            $current[$day][$i]['allow'] = 'undone';
        }
        $sortkey = array_map(function($ar) { return $ar[0];}, array_column($current[$day], 'time'));
        array_multisort($sortkey, SORT_DESC,$current[$day]);
    }
}
var_export($current);

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/01 19:51

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

    キャンセル

  • 2019/02/01 22:01

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

    キャンセル

  • 2019/02/02 03:10

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

    キャンセル

+1

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

function mymerge($arg1, $arg2, $target) {
    $newdate = key($arg1);
    $newarr = $arg1[$newdate][0];
    if ($newarr['action'] !== $target) return $arg2;

    foreach ($arg2 as $date => $arr) {
        if ($date == $newdate) {
            foreach ($arr as $n => $tarr) {
                if ($tarr['action'] === $target) {
                    array_unshift($tarr['time'], $newarr['time']);
                    array_unshift($tarr['actorID'], $newarr['actorID']);
                    $tarr['allow'] =  $newarr['allow'];
                    array_splice($arg2[$date], $n, 1);
                    array_unshift($arg2[$date], $tarr);
                    break;
                }
            }
        }
    }
    return $arg2;
}

$update = mymerge( $new, $current, 'apple');

print_r($update);

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/01 19:32

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

    キャンセル

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

  • ただいまの回答率 88.09%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る