Как вывести отсортированный хеш в шаблоне ruby

Я создаю файл конфигурации для одного из наших встроенных приложений. По сути, это файл json. У меня много проблем с получением puppet/ruby 1.8 для вывода хэша/json каждый раз одинаково.

сейчас я использую

<%= require "json"; JSON.pretty_generate data %>

Но при выводе удобочитаемого контента он не гарантирует каждый раз один и тот же порядок. Это означает, что puppet будет часто отправлять уведомления об изменениях для одних и тех же данных.

я тоже пробовал

<%= require "json"; JSON.pretty_generate Hash[*data.sort.flatten] %>

Который будет генерировать одни и те же данные/заказ каждый раз. Проблема возникает, когда данные имеют вложенный массив.

data => { beanstalkd => [ "server1", ] }

становится

"beanstalkd": "server1",

вместо

"beanstalkd": ["server1"],

Я боролся с этим в течение нескольких дней, поэтому мне нужна помощь.


person Gavin Mogan    schedule 16.03.2012    source источник


Ответы (2)


Хэш — это неупорядоченная структура данных. В некоторых языках (например, в ruby) существует упорядоченная версия хэша, но в большинстве случаев в большинстве языков не следует полагаться на какой-либо определенный порядок в хеше.

Если порядок важен для вас, вы должны использовать массив. Итак, ваш хэш

{a: 1, b: 2}

становится этим

[{a: 1}, {b: 2}]

Я думаю, это не требует слишком много изменений в вашем коде.

Обходной путь в вашей ситуации

Попробуй это:

data = {beanstalkId: ['server1'], ccc: 2, aaa: 3}

data2 = data.keys.sort.map {|k| [k, data[k]]}

puts Hash[data2]
#=> {:aaa=>3, :beanstalkId=>["server1"], :ccc=>2}
person Sergio Tulentsev    schedule 16.03.2012
comment
Порядок не имеет значения, если он каждый раз одинаков, поэтому puppet не думает, что конфигурация меняется каждый час. Переключение на массив - это не то, что я могу сделать без большого количества уродливых взломов. - person Gavin Mogan; 16.03.2012
comment
На данный момент это работает, но проблема с вложенными хэшами остается. Поскольку время, необходимое для вывода, на самом деле не проблема, я могу просто написать пользовательскую функцию ruby, которая выводит его вручную. Спасибо хоть. Все еще ближе всего к тому, чтобы быть правым. - person Gavin Mogan; 20.03.2012
comment
Это работает для моего конкретного примера, но не совсем то, о чем я говорил. Но это правильный и пока единственный ответ, так что буду, еще раз спасибо - person Gavin Mogan; 20.03.2012
comment
В Ruby 1.9 вы можете просто сделать data2 = Hash[ data.sort ], если вам нужна только неглубокая сортировка. - person Phrogz; 13.04.2015

Поскольку хэши в Ruby упорядочены, а вопрос помечен тегом ruby, вот метод, который будет рекурсивно сортировать хэш (не влияя на порядок массивов):

def sort_hash(h)
  {}.tap do |h2|
    h.sort.each do |k,v|
      h2[k] = v.is_a?(Hash) ? sort_hash(v) : v
    end
  end
end

h = {a:9, d:[3,1,2], c:{b:17, a:42}, b:2 }
p sort_hash(h)
#=> {:a=>9, :b=>2, :c=>{:a=>42, :b=>17}, :d=>[3, 1, 2]}

require 'json'
puts sort_hash(h).to_json
#=> {"a":9,"b":2,"c":{"a":42,"b":17},"d":[3,1,2]}

Обратите внимание, что это приведет к катастрофическому сбою, если ваш хеш имеет ключи, которые нельзя сравнивать. (Если ваши данные исходят из JSON, этого не будет, так как все ключи будут строками.)

person Phrogz    schedule 13.04.2015