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

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

ただいまの
回答率

87.93%

PHP配列の重複を調べるには

受付中

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 1,332

2つの配列を比較して重複しているものを調べたいのですが上手くいきません。

配列のキーに"id"・"map"・"date"があり、"id"・"map"のキーの値を比較して重複しているものを調べるにはどうしたら良いのでしょうか?
//配列1
$dataArray_a[] = array('id'=>'100','map'=>'97','date'=>'2016_04');
$dataArray_a[] = array('id'=>'100','map'=>'97','date'=>'2016_04');
//配列2
$dataArray[] = array('id'=>'100','map'=>'97','date'=>'2016_04');
$dataArray[] = array('id'=>'100','map'=>'97','date'=>'2016_05');
$dataArray[] = array('id'=>'100','map'=>'96','date'=>'2016_04');

求めている結果
Array ( 
[0] => Array ( [id] => 100 [map] => 97 [day] => 2016_05 ) 
[1] => Array ( [id] => 100 [map] => 96 [day] => 2016_04 ) ) 

”date”のキーは新しい日付が理想なのですが、処理が厳しいのであれば無視しても構いません。
お手数ですがよろしくお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • Makos

    2016/08/14 15:40

    求めている結果には、重複していないものが表示されているようなのですが・・・

    キャンセル

回答 4

+1

もっとスマートなやり方があると思いまし、
ご所望のものとは違うかもしれませんが…
あと、動作確認はしてません。
ちなみに、データの各桁が可変の場合は工夫が必要です。

function sort1($ar1, $ar2) {
  $str_ar = array();
  foreach (array_merge($ar1, $ar2) as $ar) {
    $str_ar[] = $ar['id'] . ',' . $ar['map'] . ',' . $ar['date'];
  }
  sort($str_ar, SORT_STRING);
  $res = array();
  foreach ($str_ar as $str) {
    $ar = explode(',', $str);
    $res[] = array('id'=>$ar[0], 'map'=>$ar[1], 'date'=>$ar[2]);
  }
  return $res;
}

$sorted = sort1($dataArray_a, $dataArray);
$sorted[] = array('id'=>'*', 'map'=>'*', 'date'=>'*');
$x = array_shift($sorted);
$z = array();
foreach ($sorted as $ar) {
  if ($ar['id'] != $x['id'] || $ar['map'] != $x['map']) {
    $z[] = $x;
  }
  $x = $ar;
}
var_dump($z);

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

テーブルの扱いについてはSQLが得意とするところなので、SQLite3を使用されてはいかがでしょうか。
以下、コードを載せておきます。
※実行前に、php.iniの設定でSQLite3を使用可能にしておく(extension=php_sqlite3.dllのコメントアウトを外す)必要があります。
※結果の並び順はid,mapの降順にしていますが、order by句の内容を変更することで並び順の変更も可能です。

function intersect_dataArrays($array1,$array2){
  //DB・テーブル作成
  $db = new SQLite3(':memory:');
  $sql = <<<EOD
    create table dataArray(
      arrayid string,
      id      string,
      map     string,
      date    string
    );
EOD;
  $db->exec($sql);

  //データ挿入
  foreach($array1 as $record){
    $sql = <<<EOD
      insert into dataArray values(
        "1",
        "${record['id']}",
        "${record['map']}",
        "${record['date']}"
      );
EOD;
    $db->exec($sql);
  }

  foreach($array2 as $record){
    $sql = <<<EOD
      insert into dataArray values(
        "2",
        "${record['id']}",
        "${record['map']}",
        "${record['date']}"
      );
EOD;
    $db->exec($sql);
  }

  //データ抽出
  $sql = <<<EOD
    select a.id,a.map,max(a.date) as date from dataArray a,
    (select id,map from dataArray where arrayid="1"
     intersect
     select id,map from dataArray where arrayid="2"
    ) b
    where a.id = b.id and a.map = b.map
    group by a.id,a.map
    order by a.id desc,a.map desc
    ;
EOD;
  $result = $db->query($sql);

  //データ抽出結果を配列として返す
  while($row = $result->fetchArray(SQLITE3_ASSOC)){
    //取得結果
    $ret[] = $row;
  }
  return $ret;
}

//配列1(2番目は'map'=>'96'の間違いですよね?)
$dataArray_a[] = array('id'=>'100','map'=>'97','date'=>'2016_04'); 
$dataArray_a[] = array('id'=>'100','map'=>'96','date'=>'2016_04'); 

//配列2 
$dataArray[] = array('id'=>'100','map'=>'97','date'=>'2016_04'); 
$dataArray[] = array('id'=>'100','map'=>'97','date'=>'2016_05'); 
$dataArray[] = array('id'=>'100','map'=>'96','date'=>'2016_04'); 

//結果取得
$resultArray = intersect_dataArrays($dataArray_a,$dataArray);
var_export($resultArray);
/* 出力結果
array (
  0 => 
  array (
    'id' => 100,
    'map' => 97,
    'date' => '2016_05',
  ),
  1 => 
  array (
    'id' => 100,
    'map' => 96,
    'date' => '2016_04',
  ),
)
*/

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

$items  = array_merge($array1, $array2);
$groups = array();
$result = array();

// id, mapの組ごとに振り分ける
foreach ($items as $item) {
    $group = $item['id'] . '_' . $item['map'];
    $groups[$group][] = $item;
}

// 各組を逆順でソートして先頭を取り出す
foreach ($groups as $group_items) {
    rsort($group_items);
    $result[] = $group_items[0];
}

$result;

各要素のキーの並び順がid,map,dateで揃っているなら連想配列の配列でも
sort, rsortできる(できた(知らなかった))

キーごとの値を比較しているわけではなくて、先頭同士の値、次の値...と比較されるようなので
array('id'=>'100','date'=>'2016_04','map'=>'97'); //mapとdateの順番が違う
array('id'=>'100','hoge'=>'hoge','date'=>'2016_04','map'=>'97'); //違う項目が入ってる
こういうのが混ざりこんでくると簡単におかしくなるみたいです。

ソート部分はarray_multisort(array_column($group_items, 'date'), SORT_DESC, $group_items);
としたほうが間違いは無さそう。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

こんな感じでどうでしょう。

$dataArray_a[] = array( 'id' => '100', 'map' => '97', 'date' => '2016_04' );
$dataArray_a[] = array( 'id' => '100', 'map' => '97', 'date' => '2016_04' );
$dataArray[]   = array( 'id' => '100', 'map' => '97', 'date' => '2016_04' );
$dataArray[]   = array( 'id' => '100', 'map' => '97', 'date' => '2016_05' );
$dataArray[]   = array( 'id' => '100', 'map' => '96', 'date' => '2016_04' );
$dataArray += $dataArray_a;

foreach ( $dataArray as $key => $row) {
    $dataId[ $key ]   = $row[ 'id' ];
    $dataMap[ $key ]  = $row[ 'map' ];
    $dataDate[ $key ] = $row[ 'date' ];
}
array_multisort( $dataId, SORT_ASC, $dataMap, SORT_ASC, $dataDate, SORT_DESC, $dataArray );
$res = array_reduce( $dataArray, function( $result, $item ) {
    $cnt = count( $result );
    if ( $cnt > 0 ) {
        $tmp = $result[ --$cnt ];
        if ( ( $tmp[ 'id' ] === $item[ 'id' ] ) && ( $tmp[ 'map' ] === $item[ 'map' ] ) ) {
            return $result;
        }
    }
    $result[] = $item;
    return $result;
}, [] );
var_export( $res );
/* 出力結果
    array (
      0 => 
      array (
        'id' => '100',
        'map' => '96',
        'date' => '2016_04',
      ),
      1 => 
      array (
        'id' => '100',
        'map' => '97',
        'date' => '2016_05',
      ),
    )
 */

【PHP: array_multisort - Manual】
http://php.net/manual/ja/function.array-multisort.php

【PHP: array_reduce - Manual】
http://php.net/manual/ja/function.array-reduce.php

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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