How can I sort YAML files?

前端 未结 9 931
灰色年华
灰色年华 2021-02-02 12:55

I\'ve been trying to sort an i18n translations YAML file with Ruby so I can manage new translations in a better and organized way, but I\'ve been wondering if there is something

相关标签:
9条回答
  • 2021-02-02 12:57

    In my use cases where deep sorting a hash is needed, the hash is always a tree where keys are labels and values are (sub)trees (if hashes) or leaves (otherwise). I need to deep-sort only the labels of trees (not the values).

    I got this

    before: {"a":[2,10,{"5":null,"1":null,"3":null}],"x":{"5":null,"1":null,"3":null},"a2":{"5":[2,10,5],"1":null,"3":null}}
    after:  {"a":[2,10,{"5":null,"1":null,"3":null}],"a2":{"1":null,"3":null,"5":[2,10,5]},"x":{"1":null,"3":null,"5":null}}
    

    with this

        def deeply_sort_hash(object)
          return object unless object.is_a?(Hash)
          hash = Hash.new
          object.each { |k, v| hash[k] = deeply_sort_hash(v) }
          sorted = hash.sort { |a, b| a[0].to_s <=> b[0].to_s }
          hash.class[sorted]
        end
    
    0 讨论(0)
  • 2021-02-02 13:00

    In Ruby 1.8 hashes don't have a particular order, so you cannot just sort them.

    You could monkey-patch/overwrite the to_yaml method of Hash like this:

    #!/usr/local/bin/ruby -w
    
    require 'yaml'
    
    class Hash
      def to_yaml(opts = {})
        YAML::quick_emit(self, opts) do |out|
          out.map(taguri, to_yaml_style) do |map|
            keys.sort.each do |k|
              v = self[k]
              map.add(k, v)
            end
          end
        end
      end
    end
    
    dict = YAML.load($<.read)
    
    puts dict.to_yaml
    

    Of course, the exact details may depend on your version of YAML/Ruby. The example above is for Ruby 1.8.6.

    0 讨论(0)
  • 2021-02-02 13:00

    Here's another alternative for anyone else who comes across this..

    require 'yaml'
    
    yaml = YAML.load(IO.read(File.join(File.dirname(__FILE__), 'example.yml')))
    
    @yml_string = "---\n"
    
    def recursive_hash_to_yml_string(hash, depth=0)
      spacer = ""
      depth.times { spacer += "  "}
      hash.keys.sort.each do |sorted_key|
        @yml_string += spacer + sorted_key + ": "
        if hash[sorted_key].is_a?(Hash)
          @yml_string += "\n"
          recursive_hash_to_yml_string(hash[sorted_key], depth+1)
        else
          @yml_string += "#{hash[sorted_key].to_s}\n"
        end
      end
    end
    
    recursive_hash_to_yml_string(yaml)
    
    open(File.join(File.dirname(__FILE__), 'example.yml'), 'w') { |f|
      f.write @yml_string
    }
    
    0 讨论(0)
  • 2021-02-02 13:03

    UPDATE April 2014:

    Using Rails 3.2.13, Ruby 1.9.3p489:

    I just used the i18n_yaml_sorter gem ( https://github.com/redealumni/i18n_yaml_sorter ).

    Simply add to your Gemfile:

    gem 'i18n_yaml_sorter', group: :development
    

    Then run the rake task to sort your locales' files:

    rake i18n:sort
    

    Worked perfectly, even though the gem has been last authored 2 years ago. It took 5 minutes max.

    0 讨论(0)
  • 2021-02-02 13:04

    Unfortunately YAML::quick_emit has been deprecated and is no longer available in newer builds of the Psych gem. If you want your hash keys to be sorted when serialized to yaml, you'll have to use the following monkey patch instead:

    class Hash
        def to_yaml opts={}
            return Psych.dump(self.clone.sort.to_h)
        end
    end
    
    0 讨论(0)
  • 2021-02-02 13:08

    You shouldn't use the YAML library like suggested in the other answers. It will screw up the formatting of long string values, remove your comments and spit unreadable char escapes when you use accents and special characters (which you will, since you are doing i18n). Use this gem I created:

    https://github.com/redealumni/i18n_yaml_sorter

    It will only sort the lines on the file, so everything will remain the same way it was on the original yaml (your accents, the YAML construct you used to enter the strings, indentation, etc). It will work with deeply nested yamls and results are pretty solid. The gem includes tests and it's good for ruby 1.8 or 1.9.

    It comes with a TextMate Bundle (Shift + Command + S) and a Rails rake task so you can sort the files easily and instantly in your editor. It's really fast.

    To illustrate the difference:

    Original:

      pt-BR:
        # Note how this is a nice way of inputing
        # paragraphs of text in YAML. 
        apples: >
          Maçãs são boas,
          só não coma 
          seus iPods!
        grapes: Não comemos elas.
        bananas: |
          Bananas são "legais":
            - Elas são <b> doces </b>.
            isto: não é chave
    
          Por isto todos gostam de bananas!
    

    Results by YAML::dump :

      pt-BR: 
        apples: "Ma\xC3\xA7\xC3\xA3s s\xC3\xA3o boas, s\xC3\xB3 n\xC3\xA3o coma  seus iPods!\n"
        bananas: "Bananas s\xC3\xA3o \"legais\":\n  - Elas s\xC3\xA3o <b> doces </b>.\n  isto: n\xC3\xA3o \xC3\xA9 chave\n\n\ Por isto todos gostam de bananas!\n"
        grapes: "N\xC3\xA3o comemos elas."
    

    Results by i18n_yaml_sorter:

      pt-BR:
        # Note how this is a nice way of inputing
        # paragraphs of text in YAML. 
        apples: >
          Maçãs são boas,
          só não coma 
          seus iPods!
        bananas: |
          Bananas são "legais":
            - Elas são <b> doces </b>.
            isto: não é chave
    
          Por isto todos gostam de bananas!
        grapes: Não comemos elas.
    
    0 讨论(0)
提交回复
热议问题