Как выбрать из заданного набора значений, округляя?

86
8

У меня есть рубиновый массив хэшей с двумя ключами, "уровень" и "цена". Для данной цены я хочу вернуть уровень.


Это достаточно просто с точным сопоставлением, но как я могу убедиться, что всегда имею соответствие, округляя свое входное значение до следующего значения в массиве?


Например, учитывая приведенный ниже массив, если у меня есть значение "499", я хочу вернуть "10".


tiers = [
{ tier: 0, price: 0 },
{ tier: 1, price: 50 },
{ tier: 2, price: 100 },
{ tier: 3, price: 150 },
{ tier: 4, price: 200 },
{ tier: 5, price: 250 },
{ tier: 6, price: 300 },
{ tier: 7, price: 350 },
{ tier: 8, price: 400 },
{ tier: 9, price: 450 },
{ tier: 10, price: 500 },
{ tier: 11, price: 550 },
{ tier: 12, price: 600 },
{ tier: 13, price: 650 },
{ tier: 14, price: 700 },
{ tier: 15, price: 750 },
{ tier: 16, price: 800 },
{ tier: 17, price: 850 },
{ tier: 18, price: 880 },
{ tier: 19, price: 950 },
{ tier: 20, price: 1000 }
]

Я могу получить точное соответствие с tiers.detect { |tier| tier[:price] == "500"}[:tier], но это вернет ошибку, если у меня нет соответствия. Я мог бы увеличить значение ввода до тех пор, пока не верну его, но это кажется очень неэффективным.


Я рассмотрел округление своего входного значения, но вы заметите, что приращение не всегда одно и то же (с уровня 17 до 18, цена увеличивается только на 30).

спросил(а) 2017-08-17T18:11:00+03:00 2 года, 11 месяцев назад
1
Решение
102

Вы можете перечислить все уровни в парах и оценить свою цену против пары. Что-то вроде этого:

def detect_tier(price)
tiers.each_cons(2) do |t1, t2|
next if price < t1[:price] # too low
next if price > t2[:price] # too high

return t1[:tier] if price == t1[:price] # matches tier price exactly
return t2[:tier] # "round up"
end
end

detect_tier(10) # => 1
detect_tier(100) # => 2
detect_tier(499) # => 10

ответил(а) 2017-08-17T18:19:00+03:00 2 года, 11 месяцев назад
54

Если массив большой, было бы разумно использовать режим поиска-минимума
 метода Array # bsearch_index, что уменьшит сложность времени от O (n) с последовательным поиском до O (log n).


Я предположил, что значения :price в tiers растут и что nil нужно вернуть, если тестовая цена превышает tiers.last[:price].


def round_up(tiers, price)
return nil if price > tiers.last[:price]
tiers[tiers.map { |h| h[:price] }.bsearch_index { |p| p >= price }][:tier]
end

Попробуйте.


tiers = [
{ tier: "cat", price: 0 },
{ tier: "dog", price: 50 },
{ tier: "pig", price: 100 },
{ tier: "owl", price: 150 },
{ tier: "ram", price: 300 }
]

round_up(tiers, -10) #=> "cat"
round_up(tiers, 0) #=> "cat"
round_up(tiers, 1) #=> "dog"
round_up(tiers, 49) #=> "dog"
round_up(tiers, 50) #=> "dog"
round_up(tiers, 51) #=> "pig"
round_up(tiers, 150) #=> "owl"
round_up(tiers, 250) #=> "ram"
round_up(tiers, 300) #=> "ram"
round_up(tiers, 301) #=> nil

ответил(а) 2017-08-18T07:46:00+03:00 2 года, 11 месяцев назад
39

tiers.detect{|x| x[:price] >= <required_number>}[:tier]

может работать, если уровни структурированы как заданные.


Не обрабатывает граничные условия.


tiers.detect{|x| x[:price] >= 1}[:tier] #=> 1
tiers.detect{|x| x[:price] >= 100}[:tier] #=> 2
tiers.detect{|x| x[:price] >= 499}[:tier] #=> 10
tiers.detect{|x| x[:price] >= 750}[:tier] #=> 15

ответил(а) 2017-08-17T18:29:00+03:00 2 года, 11 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема