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

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

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

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

Q&A

4回答

3891閲覧

【Perl】JSONデータの差分を取るには

nintail_fox

総合スコア69

Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

0グッド

0クリップ

投稿2016/03/24 09:11

編集2016/03/28 02:51

下記のようにJSONデータが2つあるとします。

JSON_A [ { 'id' : '1', 'name' : 'one', 'age' : '18', 'sex' : 'm' }, { 'id' : '2', 'name' : 'two', 'age' : '20', 'sex' : 'f' }, { 'id' : '6', 'name' : 'six', 'age' : '5', 'sex' : 'f' } ]
JSON_B [ { 'id' : '2', 'name' : 'two', 'age' : '20', }, { 'id' : '1', 'name' : 'one', 'age' : '18', 'sex' : 'm' }, { 'id' : '3', 'name' : 'three', 'age' : '60', 'sex' : 'm' } ]

結果として、下記を取得したいです

{ 'id' : '2', 'name' : 'two', 'age' : '20', }, { 'id' : '3', 'name' : 'three', 'age' : '60', 'sex' : 'm' }

要件としては

  • idをキーにして、データの重複を判定する
  • データの入っている順番は保証されない
  • JSON_Aから完全に重複するデータを削除し、差分のあるデータのみをJSON_Bから抽出したい

ex1) 下記は完全一致
A [{id=1,z=1,X=1},{id=2,z=2,x=2},{id=3,z=3,X=3}]
B [{id=3,z=3,X=3},{id=2,z=2,x=2},{id=1,z=1,X=1}]

ex2) 下記はid=1以外不一致
A [{id=1,z=1,X=1},{id=2,z=2,x=2},{id=3,z=3,X=3}]
B [{id=3,z=3},{id=2,z=222,x=222},{id=1,z=1,X=1},{id=4,z=4,x=4,c=4]

自分で考えてみたのですが、

  • JSON_AとJSON_Bを配列にデコード
  • 2つの配列をループ、その中で更にハッシュをループさせながらkeyとvalueを1つずつ比較する
  • 差分があるハッシュを配列にpushする

という感じで、ネストが深くあまりスマートでないのでスッキリ書く方法があれば教えて下さい。

追記:
自分が書いたコードです…。
aとbはJSONデコード済みと見て下さい。

my $diff_data; my $diff_flag = 0; my $id_match_flag = 0; foreach my $b_data (@$b) { foreach my $a_data (@$a) { if ($b_data->{'id'} eq $a_data->{'id'}) { $id_match_flag = 1; if (keys(%$b_data) ne keys(%$a_data)) { push @$diff_data, $b_data; } else { foreach my $key (keys(%$b_data)){ unless ($b_data->{$key} eq $a_data->{$key}) { $diff_flag = 1; } } } if ($diff_flag) { push @$diff_data, $b_data; $diff_flag = 0; } } } unless ($id_match_flag) { push @$diff_data, $b_data; } $id_match_flag = 0; }

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

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

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

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

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

guest

回答4

0

たまたま同じような問題を検索していたらこのエントリーを発見したので書いておきます。

JSON::MergePatch - JSON Merge Patch implementation - metacpan.org

が、ドンピシャな感じでしょうか。

データ構造の差分をとるモジュールを使うと良さそうですかね。

投稿2017/04/07 01:15

clicktx

総合スコア8

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

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

0

初回答なので、空気が読めてないかも知れません。
terion さんの回答と似てますが、
このケースだと、普通に、ハッシュを文字列化して、
キャッシュと比較するだけで回答が得られます。

perl

1use Data::Dumper ; 2# JSON => ARRAY 部分は省略し、@A, @B とする。 3sub _make_key { 4 my %h = %{$_[0]} ; 5 return join ',', map{ $_, $h{$_} } sort keys %h ; 6} 7 8my %cache = map {+( _make_key $_ ), $_ } @A ; 9 10print Dumper [ grep { ! defined $cache{ _make_key $_ } } @B ] ; 11

投稿2016/03/25 11:42

bunzaemon

総合スコア118

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

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

nintail_fox

2016/03/28 02:33

試してみましたが、JSONが完全に一致しているかどうかしか得られませんでした…。
bunzaemon

2016/03/28 12:49

こちらの手元では正しい結果が得られます。 失礼ですが、grep ブロックの中の !(ビックリマーク)を省略していませんか?
guest

0

1件ずつCSVにして、1行に連結。(CSVでなくても)
そのご、Algorithm::Diff を使用して比較
差分のCSVを JSONに変換

perl

1use Algorithm::Diff qw(diff); 2use Data::Dumper; 3use JSON; 4 5my $json_A = '[{"id" : "1", "name" : "one", "age" : "18", "sex" : "m"}, {"id" : "2", "name" : "two", "age" : "20", "sex" : "f"}, {"id" : "6", "name" : "six", "age" : "5", "sex" : "f"}]'; 6my $data_A = decode_json($json_A); 7my @line_A = (); 8foreach my $item ( @$data_A ){ 9 my @csv = ( $$item{'id'}, $$item{'name'}, $$item{'age'}, $$item{'sex'}); 10 push @line_A, join( ',', @csv); 11} 12 13my $json_B = '[{"id" : "2", "name" : "two", "age" : "20"}, {"id" : "1", "name" : "one", "age" : "18", "sex" : "m"}, {"id" : "3", "name" : "three", "age" : "60", "sex" : "m"}]'; 14my $data_B = decode_json($json_B); 15my @line_B = (); 16foreach my $item ( @$data_B ){ 17 my @csv = ( $$item{'id'}, $$item{'name'}, $$item{'age'}, $$item{'sex'}); 18 push @line_B, join( ',', @csv); 19} 20 21@sort_line_A = sort {$a <=> $b} @line_A; 22@sort_line_B = sort {$a <=> $b} @line_B; 23 24@diffs = diff( \@sort_line_A, \@sort_line_B ); 25 26@out_json = (); 27for my $dline ( @diffs ) { 28 for my $d ( @$dline ) { 29 if ( $d->[0] eq "+" ){ 30 #print $d->[0] . "\t" . $d->[2] . "--\n"; 31 my @jdata = split( ',', $d->[2]); 32 my %item = (); 33 $item{'id'} = @jdata[0] if ( @jdata[0] ); 34 $item{'name'} = @jdata[1] if ( @jdata[1] ); 35 $item{'age'} = @jdata[2] if ( @jdata[2] ); 36 $item{'sex'} = @jdata[3] if ( @jdata[3] ); 37 push @out_json, \%item; 38 39 } 40 } 41} 42print encode_json( \@out_json ) . "\n"; 43

投稿2016/03/24 10:13

編集2016/03/28 06:09
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

nintail_fox

2016/03/28 02:28

使い方が良くないのか、Algorithmだと細切れになりすぎて出来ませんでした…。
退会済みユーザー

退会済みユーザー

2016/03/28 06:37 編集

ソースを張りました。 json のkey value で連結すれば、もっとよいかも。
guest

0

投稿2016/03/24 09:32

HiroshiWatanabe

総合スコア2160

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

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

nintail_fox

2016/03/24 09:43

ありがとうございます。 単純に配列の差分だけなら取れるのですが、 配列の中のハッシュの重複…となるとご提示頂いた方法では使用できないようです。 せっかくお調べ頂いたのにすみません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問