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

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

ただいまの
回答率

90.48%

  • Python

    8603questions

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

  • iteratorパターン

    14questions

    iteratorパターンとは、オブジェクト指向プログラミングのデザインパターンです。コンテナオブジェクトの要素を列挙する手段を独立させることによって、コンテナの内部仕様に依存しない反復子を提供することを目的とします。

[Python]多次元の辞書式配列を書いた順番に取り出したい

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 4,253

Gazelle

score 124

 環境

Pythonのバージョン: 2.7

 やりたいこと

多次元の辞書式配列から書いた順を値を取り出したい。かつ、簡単に値を定義できる。

 もうちょっと詳しく

以下のような適当な辞書式の配列があるとします。
(※以下に示すデータには全く意味がありません)

sample_dict = {
    "Device" : {
        "os": ("ios", "android", "firefox", "ubuntu"),
        "size": ("A", "B", "C")
    },
    "Mode": {
        "input": {"address", "mail", "tel"},
        "output": {"start_time", "end_time"},
        "update": {
            "editor": {"title", "body"}
        }
    }
}

このデータを書いた順番に取り出して行きたいのですが、辞書式なので書いた順に取り出せるとは限りません。

試しに、先の``sample_dict```を標準出力させると、次のようになります。

{'Device': {'os': ('ios', 'android', 'firefox', 'ubuntu'), 'size': ('A', 'B', 'C')}, 'Mode': {'input': set(['mail', 'tel', 'address']), 'update': {'editor': set(['body', 'title'])}, 'output': set(['start_time', 'end_time'])}}

末尾の記述が最初に書いた順番と異なります(仕様)。これは、当方がしたいこととは異なります。

 試したこと

順序だった辞書式配列はどうやら作れるようで、ドキュメント(8.3.6. OrderedDict オブジェクト)に有るように、OrderedDictを試みました。

かりに、sample_dictをOrderedDictで表現すると、

sample_dict = OrderedDict()

device_data = OrderedDict()
device_data["os"] = ("ios", "android", "firefox", "ubuntu")
device_data["size"] =  ("A", "B", "C")

# Modeの方は省略

sample_dict["Device"] = device_data

というように、やや冗長な感じがします。

 どのように知りたいか

頑張ればOrderedDictで入力する値を定義できるのですが、なにせ書くのが面倒だなぁ、というのが正直なところです。このような経緯から質問しようと至ったわけです。

すなわち、いま達成したいことは、次の2点です。

  1. 順序だった辞書式配列(jsonでも可)でデータを定義したい
  2. 1.の時の定義をより簡潔にしたい。

もっとスマートに書く方法をご存知のかたいらっしゃいましたら、ご助力のほどよろしくお願いします。

質問の情報が不足しておりましたら、ご指摘下さい。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 4

check解決した方法

+2

 自己解決方法

teamikl様、raccy様の回答に有りますように

json.JSONDecoder(object_pairs_hook=OrderedDict).decode(raw_text)

を使って処理する方法、大変参考になりました。ただ、入力値の書き方JSONなので、やや見づらいなあと思いながら

raccy様のコメント

※ どうしても、リテラルで普通に書きたいんじゃ!と言うのであれば、Rubyに浮気するしか無いです…。

を見たところピンッと来て、ちょっと検索の方法を変えて見ました。
つまり、YAML形式で設定した値がOrderedDictで返ってくれば最高じゃないか。。。と思って検索したら、

Stack Overflow: In Python, how can you load YAML mappings as OrderedDicts?

ありました。JSONも良かったのですが、人間的にはYAMLのほうが見やすいためこちらを採用させていただきました。したがって、自己解決としようと思います。

ご回答者の皆様、回答ありがとうございました。

以下にサンプルコードを載せますので、同じ疑問を抱いた方はぜひ使って下さい。

 準備

以下の2つをpipでインストールして下さい。

pyyaml
yamlordereddictloader

 サンプルコード

※Python 2.7で動作を確認しております。

同じディレクトリ中に

  • main.py
  • od_keys.json
  • od_keys.yml

を用意します。中身は次の通り。

次のようにコードを書きます。

main.py

# -*- coding: utf-8 -*-
from collections import OrderedDict
import json
import yaml, yamlordereddictloader
import os

def load_ordinary_dict_key(file_path):
    ext = os.path.splitext(file_path)[1] # 拡張子を取得
    if ext == ".json":
        # JSON形式で値を取得する場合
        f = open(file_path)
        raw_text = f.read()
        f.close()
        return json.JSONDecoder(object_pairs_hook=OrderedDict).decode(raw_text)
    if ext == ".yml" or ext == ".yaml":
        # YAML形式で値を取得する場合
        return yaml.load(open(file_path), Loader=yamlordereddictloader.Loader)

if __name__ == '__main__':
    json_od_keys = load_ordinary_dict_key("od_keys.json")
    yml_od_keys = load_ordinary_dict_key("od_keys.yml")

    print json_od_keys
    print yml_od_keys

od_keys.json

{
  "Device": {
    "os": [
      "ios",
      "android",
      "firefox",
      "ubuntu"
    ],
    "size": [
      "A",
      "B",
      "C"
    ]
  },
  "Mode": {
    "input": [
      "address",
      "mail",
      "tel"
    ],
    "output": [
      "start_time",
      "end_time"
    ],
    "update": {
      "editor": [
        "title",
        "body"
      ]
    }
  }
}

od_keys.yml

Device:
  os:
  - ios
  - android
  - firefox
  - ubuntu
  size:
  - A
  - B
  - C
Mode:
  input:
  - address
  - mail
  - tel
  update:
    editor:
    - title
    - body

 出力

JSON Version

OrderedDict([(u'Device', OrderedDict([(u'os', [u'ios', u'android', u'firefox', u'ubuntu']), (u'size', [u'A', u'B', u'C'])])), (u'Mode', OrderedDict([(u'input', [u'address', u'mail', u'tel']), (u'output', [u'start_time', u'end_time']), (u'update', OrderedDict([(u'editor', [u'title', u'body'])]))]))])

YAML Version

OrderedDict([('Device', OrderedDict([('os', ['ios', 'android', 'firefox', 'ubuntu']), ('size', ['A', 'B', 'C'])])), ('Mode', OrderedDict([('input', ['address', 'mail', 'tel']), ('update', OrderedDict([('editor', ['title', 'body'])]))]))])

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/05/07 11:26

    あ、書く場所ココじゃない・・・・。回答は削除できないのかぁぁぁ・・・

    キャンセル

  • 2016/05/07 12:33

    いえいえ、自己解決ならここで問題ありませんよ。よりベターな方法があって、それを知ることが一番良いのであり、みんなも助かりますからね。

    キャンセル

0

設定ファイル的な用途だと ConfigParser はどうでしょう。
データの取り出し方等は変わりますが、2.7以降であればデフォルトで OrderedDict です。
辞書のネストは出来ないので、必要とするデータ構造の条件と合えばですが、
文字列のクォートを省く等、定義の記述は簡素化できます。

また、JSONであれば object_pairs_hook オプションで OrderedDict を使うよう指定可能です。
json.load(open("sample.json"), object_pairs_hook=collections.OrderedDict)

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/05/07 10:58

    teamikl様、回答ありがとうございます。

    ConfigParserを早速試しました。
    > 辞書のネストは出来ないので、...
    と仰るとおり、今回のようなデータが多層化された場合ですと不向きかな、と感じました。ただ、このようなConfigParserについては触ったことがなかったので良い勉強となりました。ありがとうございます。

    > また、JSONであれば object_pairs_hook オプションで OrderedDict を使うよう指定可能です。
    > json.load(open("sample.json"), object_pairs_hook=collections.OrderedDict)

    こちら、やりたいこととして完璧でした。object_pairs_hookを使う方法は初めて知りました。ありがとうございます。

    キャンセル

0

jsonであればこんな感じでしょうか。sample_dictとsample_ordered_dictを見比べてみてください。

from __future__ import print_function
from collections import OrderedDict
import json

sample_json = """
{
    "Device": {
        "os": ["ios", "android", "firefox", "ubuntu"],
        "size": ["A", "B", "C"]
    },
    "Mode": {
        "input": ["address", "mail", "tel"],
        "output": ["start_time", "end_time"],
        "update": {
            "editor": ["title", "body"]
        }
    }
}
"""
sample_dict = json.JSONDecoder().decode(sample_json)
print(sample_dict)
sample_ordered_dict = json.JSONDecoder(
    object_pairs_hook=OrderedDict).decode(sample_json)
print(sample_ordered_dict)

参考: stackoverflow: Can I get JSON to load into an OrderedDict in Python?

※ どうしても、リテラルで普通に書きたいんじゃ!と言うのであれば、Rubyに浮気するしか無いです…。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/05/07 11:00

    raccy様、回答ありがとうございます。

    JSONDecorderのサンプルありがとうございます。

    > ※ どうしても、リテラルで普通に書きたいんじゃ!と言うのであれば、Rubyに浮気するしか無いです…。

    確かにRubyだったらなぁ、と思いながら書いていました。

    ただ、こちらのコメント、検索の良いヒントとなりました。ありがとうございました!

    キャンセル

0

回答ではありませんが、OrderedDict についての参考情報を紹介します。

d = OrderedDict(one=1, two=2, three=3)
print d

最初のdはえっっっ!と思うが、
OrderedDict([(‘one’, 1), (‘three’, 3), (‘two’, 2)])
のように表示されてしまう。
...
追加 した順番を記憶するとあるだけです。書き込んだ順番は維持してくれない。。。
...
もっと簡単に順番も維持されてもっと簡単に書き込む方法ないの?
下記DLようにタプルにしてやると、順番通り突っ込めます。
DL = (('four',4), ('five',5), ('six',6))
d2 = OrderedDict(DL)
d1.update(d2)
print d1
for i in d1:
    print i, d1[i]    
...

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/05/07 11:00

    katoy様、回答有り難うございます。

    キャンセル

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

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

関連した質問

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

  • Python

    8603questions

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

  • iteratorパターン

    14questions

    iteratorパターンとは、オブジェクト指向プログラミングのデザインパターンです。コンテナオブジェクトの要素を列挙する手段を独立させることによって、コンテナの内部仕様に依存しない反復子を提供することを目的とします。