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

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

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

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

566閲覧

複数の多次元辞書を結合させる際にキーの重複確認をし、重複した場合は値をリストとして追加登録させるには

dd_

総合スコア111

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/12/14 06:16

編集2021/12/14 06:29

やりたい事

python

1dic1 = {"りんご":{"key1":"フルーツ", "key2":"赤"}, "バナナ":{"key1":"くだもの", "key2":"黄色"}, "ぶどう":{"key1":"果物", "key2":"紫"}} 2 3dic2 = {"メロン":{"key1":"果実", "key2":"緑"}, "ぶどう":{"key1":"くだもの", "key2":"むらさき"}, "梨":{"key1":"くだもの", "key2":"黄色"}} 4 5dic3 = {"メロン":{"key1":"くだもの", "key2":"みどり色"}, "りんご":{"key1":"果実", "key2":"真っ赤"}, "バナナ":{"key1":"フルーツ", "key2":"黄色い"}} 6 7dic4 = {"りんご":{"key1":"くだもの", "key2":"赤い"}, "さくらんぼ":{"key1":"くだもの", "key2":"赤"}, "バナナ":{"key1":"くだもの", "key2":"Yellow"}} 8

この辞書を結合させて
下のような結果にさせたいと思っております。
(改行、辞書内の辞書の順番にこだわりはありません)

python

1{{"りんご":{"key1":["フルーツ","果実","くだもの"], "key2":["赤","真っ赤"]}, 2"バナナ":{"key1":["くだもの","フルーツ","くだもの"], "key2":["黄色","黄色い","Yellow"]}, 3"ぶどう":{"key1":["果物","くだもの"], "key2":["紫","むらさき"]}, 4"メロン":{"key1":["果実","くだもの"], "key2":["緑","みどり色"]}, 5"梨":{"key1":"くだもの", "key2":"黄色"}, 6"さくらんぼ":{"key1":"くだもの", "key2":"赤"}}

試した事

私の考えたやり方だと

①dic1 を空のnew_dicに追加

②dic2のキー(りんご、バナナ、ぶどうなど)がnew_dicに存在するか1つずつ確認

もし存在するならリストに重複したキーの値(new_dicとdic2のkey1,key2の値)のをリストに格納

リストを重複したnew_dicの重複したキーの値に入れる

存在しないならそのままdic2のキーと値(辞書)をnew_dicに追加

③dic3のキーがnew_dicに存在するか1つずつ確認

もし存在するならリストに重複したキーの値(new_dicとdic3のkey1,key2の値)のをリストに格納

もしnew_dicの方で重複したキーのkey1, key2の値がリスト型なら

一つずつ取り出し空のリストに追加、続いてdic3のキーも追加

纏めたリストをnew_dicで重複したキーに対するkey1,key2にリストとして上書き

存在しないならそのままdic3のキーと値(辞書)をnew_dicに追加

これをやってみようと思い下の様に書いてみました。

python

1new_dic = {} 2for k, v in dic1.items(): 3 new_dic[k] = v 4 5key1 = [] 6key2 = [] 7for dic2_k, dic2_v in dic2.items(): 8 for new_dic_k, new_dic_v in new_dic.items(): 9 if dic2_k == new_dic_k: 10 key1.append(new_dic_v["key1"]) 11 key2.append(new_dic_v["key2"]) 12 key1.append(dic2_v["key1"]) 13 key2.append(dic2_v["key2"]) 14 new_dic_v["key1"] = key1 15 new_dic_v["key2"] = key2 16 else: 17 new_dic[dic2_k] = dic2_v 18print(new_dic)

こちらだと下のエラーが出されてしまいます。

RuntimeError: dictionary changed size during iteration

内容としては辞書でループ処理をさせて
辞書(私の場合はnew_dic)のサイズが変わってしまうとエラーになってしまうとの事なのではないかと思いました。

else:を消してやってみたり
new_dicをループをさせずに
dic2_k in new_dicなどでやってみましたが
なかなかうまくいかずこちらで質問させて頂きました。

二つの辞書(new_dic、dic2)でキーが重複したらnew_dicで重複したキーの値に追加させて
重複していなかったら
そのままキーと値をnew_dicに追加させるやり方だけでも
教えていただけると嬉しいです。
是非宜しくお願い致します。

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

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

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

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

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

guest

回答2

0

ベストアンサー決定後に失礼します。
reducedefaultdict を使います。

python3

1from functools import reduce 2from collections import defaultdict

reduce の第一引数に渡す関数mergeを以下のように作ります。

python3

1def merge(merged_dic, elementary_dic): 2 for fruit, k12_dic in elementary_dic.items(): 3 for k, lis in merged_dic[fruit].items(): 4 lis.append(k12_dic[k]) 5 6 return merged_dic

上記の merge を使って、以下により目的の内容を持つdefaultdict が得られます。

Python3

1result = reduce(merge, [dic1, dic2, dic3, dic4], defaultdict(lambda: {'key1': [], 'key2': []}))

result の内容は以下になります。

python3

1for fruit, merged_k12_dic in result.items(): 2 print(f'{fruit}:', merged_k12_dic)
  • 出力結果:

りんご: {'key1': ['フルーツ', '果実', 'くだもの'], 'key2': ['赤', '真っ赤', '赤い']}

バナナ: {'key1': ['くだもの', 'フルーツ', 'くだもの'], 'key2': ['黄色', '黄色い', 'Yellow']}
ぶどう: {'key1': ['果物', 'くだもの'], 'key2': ['紫', 'むらさき']}
メロン: {'key1': ['果実', 'くだもの'], 'key2': ['緑', 'みどり色']}
梨: {'key1': ['くだもの'], 'key2': ['黄色']}
さくらんぼ: {'key1': ['くだもの'], 'key2': ['赤']}

追記

上記の回答コードで、result の各キーである果物の名前に対するdictの中で、key1あるいはkey2の値であるリストの長さが1であるもののみ、リストではなくリストの先頭要素の文字列に変換する処理が抜けていました。

以下の一行を追加します。

diff

1 result = reduce(merge, [dic1, dic2, dic3, dic4], defaultdict(lambda: {'key1': [], 'key2': []})) 2 3+ result = {fruit: {k: v[0] if len(v) == 1 else v for k, v in dic.items()} for fruit, dic in result.items()} 4 5 # 結果の確認 6 for fruit, merged_k12_dic in result.items(): 7 print(f'{fruit}:', merged_k12_dic) 8
  • 出力結果:

りんご: {'key1': ['フルーツ', '果実', 'くだもの'], 'key2': ['赤', '真っ赤', '赤い']}

バナナ: {'key1': ['くだもの', 'フルーツ', 'くだもの'], 'key2': ['黄色', '黄色い', 'Yellow']}
ぶどう: {'key1': ['果物', 'くだもの'], 'key2': ['紫', 'むらさき']}
メロン: {'key1': ['果実', 'くだもの'], 'key2': ['緑', 'みどり色']}
梨: {'key1': 'くだもの', 'key2': '黄色'}
さくらんぼ: {'key1': 'くだもの', 'key2': '赤'}

投稿2021/12/14 07:47

編集2021/12/14 09:46
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

dd_

2021/12/14 09:32

ご丁寧にありがとうございます! reduce初めて見ました!今まで複数リストをfor文で処理させてたとこをreduceで短文化できそうです。ありがとうございます。 reduce(関数、 イテラブル、 オプション)の使い方も理解できました! 本当にありがとうございますm(._.)m
dd_

2021/12/14 09:35

replit.comと言うのも初めて見ました。ご丁寧にありがとうございます。これからも日々精進してまいります!
退会済みユーザー

退会済みユーザー

2021/12/14 09:49

コメントありがとうございます。 質問にある要件の中で見落としていた部分を追記しました。reduce 使いこなせると便利ですよ〜????
dd_

2021/12/14 09:56 編集

こちらこそありがとうございます! 追記も拝見しました!ありがとうございます。reduce()早速使わせてもらってます!reduce()以外にもfor文の構成なども参考にさせて頂きます!
guest

0

ベストアンサー

重複しているかどうかに関係なく、一旦リストとして登録しておき
後からリストの要素数が1の物を変換するのが簡単だと思います。
(ただし、個人的には要素数が1であってもリストのまま保持しておく方が後々の使い勝手が良いとは思いますが)

python

1from collections import defaultdict 2 3dic1 = {"りんご":{"key1":"フルーツ", "key2":"赤"}, "バナナ":{"key1":"くだもの", "key2":"黄色"}, "ぶどう":{"key1":"果物", "key2":"紫"}} 4dic2 = {"メロン":{"key1":"果実", "key2":"緑"}, "ぶどう":{"key1":"くだもの", "key2":"むらさき"}, "梨":{"key1":"くだもの", "key2":"黄色"}} 5dic3 = {"メロン":{"key1":"くだもの", "key2":"みどり色"}, "りんご":{"key1":"果実", "key2":"真っ赤"}, "バナナ":{"key1":"フルーツ", "key2":"黄色い"}} 6dic4 = {"りんご":{"key1":"くだもの", "key2":"赤い"}, "さくらんぼ":{"key1":"くだもの", "key2":"赤"}, "バナナ":{"key1":"くだもの", "key2":"Yellow"}} 7 8data = [dic1, dic2, dic3, dic4] 9res = defaultdict(lambda: defaultdict(list)) 10for d in data: 11 for k, v in d.items(): 12 for kk, vv in v.items(): 13 res[k][kk].append(vv) 14 15# リストの要素数が1のものを変換 16for k, v in res.items(): 17 for kk, vv in v.items(): 18 if len(vv) == 1: 19 res[k][kk] = vv[0] 20 21print(res['りんご']['key1']) 22print(res['さくらんぼ']['key2'])

投稿2021/12/14 06:53

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

dd_

2021/12/14 07:28

ご回答ありがとうございます。 辞書でもzip関数の様なものが無いかなと思ってたのですが 一度すべての辞書をリストに纏めてやるやり方があったんですね! 無理矢理やってみようと思ってましたが ftlobw様のやり方が超絶簡単ですね! 質問する前にもっと調べるべきでした。とても勉強になりました。 本当にありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問