2016-07-29 11 views
1

私は、いくつかのファイルにあるすべてのテキストを新しい単数形のYAMLファイルに入れて、誰かがスペイン語に翻訳できる英語の翻訳を入れることに取り組んでいます。ネストされた辞書の値と共にすべてのキーを返す

各YAMLファイルには多数のネストされたテキストがあります。 YAMLファイルの各値に対して、値とともに、完全な 'パス'、つまりすべてのキーを出力したいとします。ここでmyproject.section.more_informationファイルに住んでいる.yamlファイルの例入力です:

default: 
    heading: Here’s A Title 
    learn_more: 
     title: Title of Thing 
     url: www.url.com 
     description: description 
     opens_new_window: true 

が、ここで所望の出力があります:

myproject.section.more_information.default.heading: Here’s a Title 
myproject.section.more_information.default.learn_more.title: Title of Thing 
mproject.section.more_information.default.learn_more.url: www.url.com 
myproject.section.more_information.default.learn_more.description: description 
myproject.section.more_information.default.learn_more.opens_new_window: true 

これは再帰のための良い候補のように思えるので、私this answer

などの例を見てみましたが、値の最後のキーだけでなく、特定の値につながるすべてのキーを保持したいと考えています。私は現在PyYAMLを使ってYAMLを読み書きしています。

各キーを保存する方法についてのヒント項目が辞書であるかどうかを確認してから、各値に関連付けられたすべてのキーを返します。

答えて

0

ネストされたディクショナリをウォークすると再帰が必要となり、 "prefix"を "path"に渡すことで、パスのセグメント(@Pruneのように)に何も操作する必要がなくなります。

  • あなたが処理する必要が複数のファイルで同じパスにつながることができ、複数のファイルを使用しているので、(少なくとも投げ:

    は面白い、この問題を作る心に留めておくべきいくつかの事がありますエラーの場合は、データが失われる可能性があります)。私の例では、値のリストを生成します。

  • 特別なキー(非文字列(?)、空の文字列、.を含むキー)。私の例は、これらと出口を報告します。

ruamel.yaml¹使用してコード例:追加の入力ファイルがある場合は

import sys 
import glob 
import ruamel.yaml 
from ruamel.yaml.comments import CommentedMap, CommentedSeq 
from ruamel.yaml.compat import string_types, ordereddict 

class Flatten: 
    def __init__(self, base): 
     self._result = ordereddict() # key to list of tuples of (value, comment) 
     self._base = base 

    def add(self, file_name): 
     data = ruamel.yaml.round_trip_load(open(file_name)) 
     self.walk_tree(data, self._base) 

    def walk_tree(self, data, prefix=None): 
     """ 
     this is based on ruamel.yaml.scalarstring.walk_tree 
     """ 
     if prefix is None: 
      prefix = "" 
     if isinstance(data, dict): 
      for key in data: 
       full_key = self.full_key(key, prefix) 
       value = data[key] 
       if isinstance(value, (dict, list)): 
        self.walk_tree(value, full_key) 
        continue 
       # value is a scalar 
       comment_token = data.ca.items.get(key) 
       comment = comment_token[2].value if comment_token else None 
       self._result.setdefault(full_key, []).append((value, comment)) 
     elif isinstance(base, list): 
      print("don't know how to handle lists", prefix) 
      sys.exit(1) 

    def full_key(self, key, prefix): 
     """ 
     check here for valid keys 
     """ 
     if not isinstance(key, string_types): 
      print('key has to be string', repr(key), prefix) 
      sys.exit(1) 
     if '.' in key: 
      print('dot in key not allowed', repr(key), prefix) 
      sys.exit(1) 
     if key == '': 
      print('empty key not allowed', repr(key), prefix) 
      sys.exit(1) 
     return prefix + '.' + key 

    def dump(self, out): 
     res = CommentedMap() 
     for path in self._result: 
      values = self._result[path] 
      if len(values) == 1: # single value for path 
       res[path] = values[0][0] 
       if values[0][1]: 
        res.yaml_add_eol_comment(values[0][1], key=path) 
       continue 
      res[path] = seq = CommentedSeq() 
      for index, value in enumerate(values): 
       seq.append(value[0]) 
       if values[0][1]: 
        res.yaml_add_eol_comment(values[0][1], key=index) 


     ruamel.yaml.round_trip_dump(res, out) 


flatten = Flatten('myproject.section.more_information') 
for file_name in glob.glob('*.yaml'): 
    flatten.add(file_name) 
flatten.dump(sys.stdout) 

default: 
    learn_more: 
     commented: value # this value has a comment 
     description: another description 

を、結果は次のとおりです。もちろん

myproject.section.more_information.default.heading: Here’s A Title 
myproject.section.more_information.default.learn_more.title: Title of Thing 
myproject.section.more_information.default.learn_more.url: www.url.com 
myproject.section.more_information.default.learn_more.description: 
- description 
- another description 
myproject.section.more_information.default.learn_more.opens_new_window: true 
myproject.section.more_information.default.learn_more.commented: value # this value has a comment 

もしあなた入力に二重パスがない場合、出力には何もありませんリスト。 ruamel.yamlからstring_typesordereddictを使用することにより

は(あなたが使用しているバージョンを示していない)、このPython2とのpython3への互換性が高まります。

ordereddictは元のキーの順序を保持しますが、これはもちろんファイルの処理順序に依存します。あなたはパスがソートしたい場合は、単に使用するdump()を変更:

 for path in sorted(self._result): 

はまた、上のコメントは、辞書のエントリが保存されている「コメント」ことに注意してください。


ruamel.yamlはラウンドトリップにコメントや他のデータを保存YAML 1.2パーサー(PyYAMLとはYAML 1.1の大部分を行う)である¹しました。免責事項:私はruamel.yamlの著者です

+0

これは、私の意見が持っている可能性のある多くの重要なケースに対する非常によく考えられた解決策です。ありがとうございます。私は特にそれがコメントを保存するのが好きです。 – swellactually

0

あなたがしたいことは、ネストされた辞書を平坦化することです。 Flatten nested Python dictionaries, compressing keys

実際には、sep引数を.に変更しただけで、上の回答のコードスニペットがうまくいくと思います。

編集:SOあなたは順番にそれをしたい場合は、あなたががOrderedDictが必要になりますhttp://ideone.com/Sx625B

import collections 

some_dict = { 
    'default': { 
     'heading': 'Here’s A Title', 
     'learn_more': { 
      'title': 'Title of Thing', 
      'url': 'www.url.com', 
      'description': 'description', 
      'opens_new_window': 'true' 
     } 
    } 
} 

def flatten(d, parent_key='', sep='_'): 
    items = [] 
    for k, v in d.items(): 
     new_key = parent_key + sep + k if parent_key else k 
     if isinstance(v, collections.MutableMapping): 
      items.extend(flatten(v, new_key, sep=sep).items()) 
     else: 
      items.append((new_key, v)) 
    return dict(items) 

results = flatten(some_dict, parent_key='', sep='.') 
for item in results: 
    print(item + ': ' + results[item]) 

に答えるリンクに基づいて取り組ん例えば

確認し、これを。

+0

スーパー、送信ありがとうございます。私は見て、出力が素晴らしいように見えた。残念ながら、私はコンピュータから離れなければなりませんが、今週末に見ています。 – swellactually

+0

このリンクは質問に答えるかもしれませんが、回答の重要な部分をここに含めて参考にしてください。リンクされたページが変更された場合、リンクのみの回答は無効になることがあります。これは他のStackOverflowの回答へのリンクでも起こります。 – Anthon

+1

Gotcha @Anthon、私はそれを修正します。 – Michael

0

各インデントの深さで最も新しいキーである文字列の単純なリストを保持します。変更しないで1つの行から次の行に進むときは、リストの最後にある項目を変更するだけです。あなたが「外に出たとき」、最後の項目をリストからポップします。インデントしたら、リストに追加します。

その後、あなたはコロンを打つたびに、対応するキー項目は、リスト内の文字列を連結し、のようなものです:あなたは立派な速度で移動します

'.'.join(key_list) 

していますか?

+0

スマートなデザイン。私はコンピュータから離れなければならないので、今それを試すことができませんが、私はアプローチが好きです、ありがとう! – swellactually

+0

文字列のリストを使用すると、事が過度に複雑になります。とにかくネストされた辞書を掘り下げて再帰を使わなければならないので、現在のパス "接頭辞"を渡してください。そして、再帰はあなたが手で行うことを提案する/ポッピングを処理します。 – Anthon

関連する問題