Проблема с вашим кодом (за исключением пропущенного оператора end
, который, как я ожидаю, не выдержал операции вырезания и вставки), заключается в том, что хэши не имеют ключей "payment_method"
и "amount"
(charges.first.keys #=> [:payment_method, :amount]
).: -)
С исправлением ваш код работает нормально:
charges.sort_by {|h| [["card", "stripe"].include?(h[:payment_method]) ? 0 : 1, h[:amount]]}
# [{:payment_method=>"stripe", :amount=> 500},
# {:payment_method=>"card", :amount=>1000},
# {:payment_method=>"stripe", :amount=>1500},
# {:payment_method=>"card", :amount=>2000},
# {:payment_method=>"cash", :amount=> 200},
# {:payment_method=>"cash", :amount=>4000}]
Давайте посмотрим поближе на то, что делал ваш код.
Поскольку у хэшей нет ключей"paymment_method"
и "amount"
, obj["paymment_method"] #=> nil
и obj["amount"] #=> nil
для всех obj
.Следовательно,
["card","stripe"].include? obj["payment_method"]
становится
["card","stripe"].include? nil
, что составляет false
для всех obj
.Таким образом, первый элемент массива сортировки, используемый Enumerable # sort_by , всегда 1
.
sort_by
использует Array # <=> для упорядочивания массивов. 1 При сравнении obj1["amount"]
и obj2["amount"]
используется метод экземпляра <=>
, определенный для класса obj1
и obj2
.В этом случае
obj1["amount"] <=> obj2["amount"] #=> nil <=> nil
Мы видим из
nil.method(:<=>).owner #=> Kernel
Array.ancestors #=> [Array, Enumerable, Object, Kernel, BasicObject]
, что NilClass
имеет метод экземпляра <=>
, который он получает из Kernel
( Object # <=> 2 ), который возвращает ноль всякий раз, когда сравниваемые объекты равны (здесь nil <=> nil #=> 0
).Таким образом, сравнение сортировки всегда [1, nil] <=> [1, nil]
, что означает, что сортировка фактически случайна.
1 См. Последний документ - в частности, третий абзац - для объяснения того, как это происходит.готово.
2 Чтобы понять, почему Kernel#<=>
задокументировано в Объект , см. третий абзац по ссылке.