やりたいこと
>>> dict_in_list1
[
{'name': 'Reimu', 'spells': ['Musouhuin', 'niju-kekkai']},
{'name': 'Marisa', 'spells': ['non-directional laser', 'star-dust reverie']},
{'name': 'Alice', 'spells': ['hourai-doll', 'shanghai-doll']}
]
>>> dict_in_list2
[
{'name': 'Marisa', 'spells': ['star-dust reverie', 'non-directional laser']},
{'name': 'Reimu', 'spells': ['Musouhuin', 'niju-kekkai']},
{'name': 'Alice', 'spells': ['hourai-doll', 'shanghai-doll']}
]
- dictを要素として持つlistがあり、「同じデータ」かを比べたい.
- ただし並び順は異なっていても良いとする。
- dictのあるvalue(下例ではspell)もlistとなっている。
- こちらの要素も並び順は異なっていても良いとする。
2の要件は、name: Marisa 要素のspells部分のことです。順番が逆になってますが、これは「同じデータ」として扱いたいです.
'spells': ['non-directional laser', 'star-dust reverie']
'spells': ['star-dust reverie', 'non-directional laser']
解決方法
DeepDiffを使うと、以下のようにして同じデータであるかを比較できる.
$ pip install deepdiff
pytest
assert not DeepDiff(dict_in_list1, dict_in_list2, ignore_order=True)
unittest
self.assertEqual(DeepDiff(dict_in_list1, dict_in_list2, ignore_order=True), {})
その他の方法
もし、要素であるdictのvalueがstrなどだった場合は、sortedで並び替える(おすすめ) or assertCountEqualメソッドを使うのもアリだと思います。
以下のような単純な例だと、key引数を指定したsortを使う方が明瞭なのでおすすめです。
names1 = [
{"name": "Reimu"},
{"name": "Marisa"},
{"name": "Alice"},
]
names2 = [
{"name": "Alice"},
{"name": "Reimu"},
{"name": "Marisa"},
]
>>> from unittest import TestCase
>>> case = TestCase()
>>> case.assertCountEqual(names1, names2) # OK!!
assertCountEqualはその名前から↑のような比較ができるようには見えないですが、問題なく機能します。
余談
assertCountEqualメソッドの本来の使い方は、おそらく以下のようなケース(要素ごとの数え上げ)だと思います.(多分)
>>> fruits1 = ["りんご", "みかん", "みかん", "りんご", "りんご"]
>>> fruits2 = ["りんご", "みかん", "みかん", "りんご", "みかん"]
>>> case.assertCountEqual(fruits1, fruits2)
AssertionError: Element counts were not equal:
First has 3, Second has 2: 'りんご'
First has 2, Second has 3: 'みかん'
メッセージもわかりやすいですね。