【问题标题】:How can I sort YAML files?如何对 YAML 文件进行排序?
【发布时间】:2011-11-08 17:02:43
【问题描述】:

我一直在尝试使用 Ruby 对 i18n 翻译 YAML 文件进行排序,以便以更好和更有条理的方式管理新翻译,但我一直想知道是否有什么可以减轻这项任务的。

我找到了一个 YAML 文件编写器,因此我可以将哈希写入文件,但我的问题是正确排序哈希。如果我得到了哈希hh.sort 返回一个数组,我还没有想出一个简单的方法来做到这一点。

我有这样的 YAML 文件:

pt-br:    
  global:
    misc:
      total: "Total"
      all: "Todos"
      close: "Fechar"
      cancel: "Cancelar"

    crud:
      access: "Acessar"
      back: "Voltar"
      edit: "Editar"
      confirm: "Confirmar"
      send: "Enviar"

...

(文件比这个大)

但我想这样对它们进行排序:

pt-br:    
  global:
    crud:
      access: "Acessar"
      back: "Voltar"
      confirm: "Confirmar"
      edit: "Editar"
      send: "Enviar"

    misc:
      all: "Todos"
      cancel: "Cancelar"
      close: "Fechar"          
      total: "Total"

我认为一些简单的递归方法可以像这样帮助我:

def translation_sort(h)
  if h.class == Hash
    h = h.sort
    h.each{|item| translation_sort(item)}
  end
  h
end

require "yaml"
h=YAML.load_file(File.open("~/pt-br.sample.yml"))
translation_sort(h)

【问题讨论】:

  • 有相同的 sublime 插件吗?

标签: ruby sorting internationalization yaml


【解决方案1】:

其实这是一个很好的问题。您想要对哈希进行深度排序。所以我不喜欢重新发明轮子,然后我搜索了一个好的实现,我找到了一个我喜欢的。看看https://gist.github.com/1083930。它工作正常。

【讨论】:

  • 这个解决方案有个小问题:它还对值进行排序。
  • 我喜欢这个解决方案,但我的 Yaml 文件中没有双引号(看起来像这样:sub_diagnostics_count: ! '# Sous-diagnostics',它仍然被解释为字符串,但看起来很糟糕)。有什么解决方案可以纠正@lucapette 吗?
  • 回答我自己和其他好奇的人:请参阅下面的回答
  • 我的变体解决了此答案gist.github.com/codeitagile/… 中引用的要点中缺少的array.sort - 如果您希望对所有内容进行排序(键加数组值)
【解决方案2】:

在 Ruby 1.8 中,哈希没有特定的顺序,因此您不能只对它们进行排序。

您可以像这样对Hashto_yaml 方法进行猴子补丁/覆盖:

#!/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

当然,具体细节可能取决于您的 YAML/Ruby 版本。上面的示例适用于 Ruby 1.8.6。

【讨论】:

  • YAML::quick_emit 已弃用且不再可用。
【解决方案3】:

对于遇到此问题的其他人来说,这是另一种选择..

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
}

【讨论】:

    【解决方案4】:

    您不应像其他答案中建议的那样使用 YAML 库。当您使用重音符号和特殊字符时(您会这样做,因为您正在使用 i18n),它会破坏长字符串值的格式,删除您的 cmets 并吐出不可读的字符转义符。 使用我创建的这个 gem:

    https://github.com/redealumni/i18n_yaml_sorter

    它只会对文件中的行进行排序,因此所有内容都将保持与原始 yaml 相同的方式(您的口音、用于输入字符串的 YAML 结构、缩进等)。它将与深度嵌套的 yaml 一起使用,结果非常可靠。 gem 包含测试,它适用于 ruby​​ 1.8 或 1.9。

    它带有一个 TextMate Bundle (Shift + Command + S) 和一个 Rails rake 任务,因此您可以在编辑器中轻松快速地对文件进行排序。真的很快。

    为了说明区别:

    原文:

      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!
    

    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."
    

    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.
    

    【讨论】:

    • 我刚刚尝试使用这个 gem 来对 config.yml 文件进行排序,但它没有用。结果已排序,但也有所不同。
    • 糟糕,多年后,我再次滑倒在这颗宝石上。这个 gem 不起作用的原因是它忽略了引用,这意味着依赖项没有正确排序。要使您的 YAML 文件对键排序具有弹性,请以在被引用之前出现的方式重命名命名键。 EG:(1)首先我将some_key: &amp;some_name重命名为_some_key: &amp;some_name并添加了some_key: \n &lt;&lt;: *some_name。 (2) 然后我将所有命名键及其值移到文件顶部,并在需要的键上添加数字。请注意,此手动调整只需要一次。
    • i18n_yaml_sorter 似乎不再工作 github.com/redealumni/i18n_yaml_sorter/issues/16
    【解决方案5】:

    在我需要对哈希进行深度排序的用例中,哈希始终是一棵树,其中键是标签,值是(子)树(如果是哈希)或叶子(否则)。我只需要对树的标签(而不是值)进行深度排序。

    我知道了

    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}}
    

    有了这个

        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
    

    【讨论】:

    • 我在寻找内置方法(或 ActiveSupport 方法)时偶然发现了这个答案。由于似乎还没有,我很高兴找到这个回复。我只想在这里发表评论,如何在新的 ruby​​ 版本中以更少的行实现同样的事情:def hash_deep_sort(element); return element unless element.is_a?(Hash); hash = element.transform_values { |value| hash_deep_sort(value) }; hash.sort.to_h; end(这不应该是一个单行,它只是 stackoverflow 迫使我不要在评论中使用多行.. .)
    • 确认可以在 Ruby 2.7.1 中工作,无需任何更改。
    【解决方案6】:

    这可能是另一个吸引人的选项:https://github.com/redealumni/i18n_yaml_sorter

    【讨论】:

    • 嗨,戈登!感谢您的回答...此 git 存储库也已包含在第二个答案中。
    【解决方案7】:

    2014 年 4 月更新:

    使用 Rails 3.2.13、Ruby 1.9.3p489:

    我刚刚使用了 i18n_yaml_sorter gem (https://github.com/redealumni/i18n_yaml_sorter)。

    只需添加到您的 Gemfile 中

    gem 'i18n_yaml_sorter', group: :development
    

    然后运行 rake 任务对您的语言环境文件进行排序:

    rake i18n:sort
    

    完美运行,尽管该 gem 的最后一次创作是在 2 年前。最多需要 5 分钟。

    【讨论】:

      【解决方案8】:

      还有一个 atom 包也可以做到这一点https://github.com/akfernun/yaml-sortkeys

      【讨论】:

      • 始终欢迎提供指向潜在解决方案的链接,但请add context around the link,以便您的其他用户知道它是什么以及为什么存在。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。考虑到仅仅是指向外部站点的链接Why and how are some answers deleted? 的一个可能原因。
      【解决方案9】:

      很遗憾,YAML::quick_emit 已被弃用,并且在 Psych gem 的较新版本中不再可用。如果您希望在序列化为 yaml 时对哈希键进行排序,则必须改用以下猴子补丁:

      class Hash
          def to_yaml opts={}
              return Psych.dump(self.clone.sort.to_h)
          end
      end
      

      【讨论】:

        猜你喜欢
        • 2017-06-14
        • 2019-10-27
        • 2018-12-25
        • 2019-09-30
        • 1970-01-01
        • 1970-01-01
        • 2014-01-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多