i-focus i-focus BB
    • カテゴリ
    • 最近
    • タグ
    • 人気
    • ユーザー
    • グループ
    • 登録
    • ログイン
    1. ホーム
    2. i_yamasaki
    3. ベスト
    • プロフィール
    • フォロー中 0
    • フォロワー 0
    • スレッド 4
    • 投稿 5
    • ベスト 3
    • Controversial 0
    • グループ 0

    i_yamasakiのベストな投稿

    • RE: 今日の学び:集合型に辞書型を入れることはできない

      集合型にはミュータブル型(変更不可)しか入れられないのが原因らしい。

      辞書はイミュータブル型(変更可能)だから入らなかったので一度文字列(ミュータブル型)に変換して入れた後にリストに入れれば疑似的に重複を削除できた。

      import json
      
      dlist = [
          {"a": 1},
          {"a": 1}, # 同じ値
          {"b": 2},
      ]
      
      dlist_new = [json.loads(d_str) for d_str in {json.dumps(d_dict) for d_dict in dlist}]
      # dlist_new = [{"a": 1}, {"b": 2}]
      # dlist_newの中身は順不同
      

      ミュータブル型に変換するのであれば辞書のキーにしてしまい値だけ取り出す方法でもできることに気が付いた。こちらのほうがすっきりしていて見やすい気がする。また、愚直に集合型を使用しないので中身が元のリストの出現順になる。

      import json
      
      dlist = [
          {"a": 1},
          {"a": 1}, # 同じ値
          {"b": 2},
      ]
      
      dlist_new = list({json.dumps(d): d for d in dlist}.values())
      
      print(dlist_new)
      # dlist_new = [{"a": 1}, {"b": 2}]
      

      辞書の要素の順がすべて同じであると保証されている場合はこれでも良いが、よく考えると要素の出現順が違うとこの方法だとうまくいかないことに気が付いてしまった。

      import json
      
      dlist = [
          {"c": 3, "a": 1},
          {"a": 1, "c": 3}, # 同じ値、ただし順番は違う
          {"b": 2, "c": 3},
      ]
      
      dlist_new1 = [json.loads(d_str) for d_str in  {json.dumps(d_dict) for d_dict in dlist}]
      # [{'c': 3, 'a': 1}, {'a': 1, 'c': 3}, {'b': 2, 'c': 3}]
      # 重複削除失敗
      
      dlist_new2 = list({json.dumps(d): d for d in dlist}.values())
      # [{'c': 3, 'a': 1}, {'a': 1, 'c': 3}, {'b': 2, 'c': 3}]
      # 重複削除失敗
      

      辞書の順をソートしても良いが、ネストが深い場合にどうにもならないので比較するしかなさそう。すでに出てきた辞書を追加するリストを使う方法が最初に浮かんだ。

      dlist = [
          {"c": 3, "a": 1},
          {"a": 1, "c": 3}, # 同じ値、ただし順番は違う
          {"b": 2, "c": 3},
      ]
      
      dlist_new = []
      
      for d in dlist:
          if d not in dlist_new:
              dlist_new.append(d)
      
      # dlist_new = [{'c': 3, 'a': 1}, {'b': 2, 'c': 3}]
      

      ただどうしてもワンライナーで書きたかったので考えていたところ、dlist内の比較結果が同じもので作成したリストを文字列に変換し、それをキーにしてリスト内の比較結果が同じものを紐づける方法を思いついた。

      import json
      
      dlist = [
          {"c": 3, "a": 1},
          {"a": 1, "c": 3}, # 同じ値、ただし順番は違う
          {"b": 2, "c": 3},
      ]
      
      dlist_new = list({json.dumps([_d for _d in dlist if d == _d]): d for d in dlist}.values())
      # [{'a': 1, 'c': 3}, {'b': 2, 'c': 3}]
      # 同じ内容の場合後に出てきたものが入る
      

      あまり賢い方法には思えないがこれが一番スッキリしていると思う。

      Pythonに投稿されました
      i_yamasakiI
      i_yamasaki
    • テスト

      投稿テストを兼ねて
      Pythonサブカテゴリ作成

      Python

      Pythonに投稿されました python
      i_yamasakiI
      i_yamasaki
    • 深いネストを持った辞書型から安全かつ簡潔に値を取り出したいので調べた

      結論

      jmespathライブラリを使うと良い

      やりたいこと

      今まで深いネストをした辞書から値を取り出す際には

      d = {"hoge": {"fuga": {"piyo": 1}}}
      
      v = d.get("hoge", {}).get("fuga", {}).get("piyo")
      # v = 1
      

      としていたが、非常に長ったらしいのでやめたかった。
      ネストをピリオドでつないで取得できるのが理想。

      調べたところ、ライブラリでの解決策としては「辞書型を拡張したクラスを使う」パターンと、「関数を通して辞書から指定の値を取り出す」パターンがあるらしい。

      「辞書型を拡張したサブクラスを使う」パターン

      pip install python-box
      
      from box import Box
      
      d = {"hoge": {"fuga": {"piyo": 1}}}
      
      mybox = Box(d, box_dots=True, default_box=True)
      v = mybox["hoge.fuga.piyo"]
      # v = <Box: 1>
      

      default_box=Trueでパスが存在しないときにも空のBoxオブジェクトが帰ってくる。
      想像していたものに近いがサブクラスの宣言がいちいち必要なのが面倒。

      「関数を通して辞書から指定の値を取り出す」パターン

      pip install jmespath
      
      import jmespath
      
      d = {"hoge": {"fuga": {"piyo": 1}}}
      
      v = jmespath.search("hoge.fuga.piyo", d)
      # v = 1
      

      これでよさそう。

      参考ページ
      https://qtatsu.hatenablog.com/entry/2021/05/08/165510

      Pythonに投稿されました python
      i_yamasakiI
      i_yamasaki