Исправьте мой код: Sort_by 2 атрибута, первый атрибут относится ко второму атрибуту, если он является одним из двух значений - PullRequest
0 голосов
/ 24 октября 2018

Дано:

charges = [
  {payment_method:"card",amount:1000},
  {payment_method:"stripe",amount:500},
  {payment_method:"stripe",amount:1500},
  {payment_method:"card",amount:2000},
  {payment_method:"cash",amount:200},
  {payment_method:"cash",amount:4000},
]

Я хочу отсортировать по 2 правилам:

  1. Сначала должны отображаться способы оплаты картой ИЛИ с полосой, а затем сортироваться по сумме, наименьшаяк наивысшему
  2. Любые другие способы оплаты должны отображаться последними, а затем сортироваться по сумме, от наименьшего к наибольшему

Мой текущий код перемещает карту или полосу вперед, но несортировка по сумме:

charges.sort_by do |obj| 
  [ 
    (["card","stripe"].include? obj["payment_method"]) ? 0 : 1 , 
    obj["amount"]
  ]

# result of MY sort:
charges = [
  {:payment_method=>"card", :amount=>1000},
  {:payment_method=>"stripe", :amount=>500},
  {:payment_method=>"stripe", :amount=>1500},
  {:payment_method=>"card", :amount=>2000},
  {:payment_method=>"cash", :amount=>200},
  {:payment_method=>"cash", :amount=>4000},
]

# result of DESIRED sort:
charges = [
  {: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},
]

1 Ответ

0 голосов
/ 24 октября 2018

Проблема с вашим кодом (за исключением пропущенного оператора 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#<=> задокументировано в Объект , см. третий абзац по ссылке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...