2015-12-29 2 views
3

Я пытаюсь найти лучший способ определить, в каком диапазоне задано целое число. Возьмите этот хэш, например:Проверить на целое число в наборе диапазонов

score_levels = { 
    1 => {'name' => 'Beginner', 'range' => 0..50}, 
    2 => {'name' => 'Intermediate', 'range' => 51..70}, 
    3 => {'name' => 'Pro', 'range' => 71..85}, 
    4 => {'name' => 'Expert', 'range' => 86..96}, 
    5 => {'name' => 'Master', 'range' => 97..100} 
} 

Я хотел бы запустить другую логику баллами, что-то вроде:

case score 
when score_levels[1]['range'] 
    level_counters[1] += 1 
when score_levels[2]['range'] 
    level_counters[2] += 1 
when score_levels[3]['range'] 
    level_counters[3] += 1 
end 

есть более общий способ сделать это? Возможно, что-то в этом духе:

score_levels.each |key, val| {if val['range'].member?(score) then level_counters[key] += 1 } 

Спасибо!

+0

Домены перекрываются и сколько из них вы ожидаете? – Vasfed

+0

Диапазоны не перекрываются. Я думаю, что-то вроде 5 диапазонов, может быть, немного больше –

+0

Хороший вопрос. Хотя это может показаться незначительным, читатели ценят, что вы назначаете переменную ('score_levels') в хэш вашего примера. Слишком часто это опускается, и даже скобки хэша могут отсутствовать. Как правило, при подаче примера (хорошая вещь) полезно показать ожидаемый или желаемый результат. Здесь, например, у вас может быть небольшой массив баллов и показывать уровни, на которые вы хотите их сопоставить. Да, это очевидно здесь, но это не занимает много места. –

ответ

3

Да, является.

level_counters[score_levels.find{|_, h| h["range"].include?(score)}.first] += 1 
+1

Сокращенно не имеет пояснительного текста. –

+2

Я вижу, что вы использовали 'include?', А не 'cover? ', Если конечные точки диапазона не являются числовыми, но поскольку они совпадают, когда диапазон является числовым, потери эффективности нет. Что делать, если «оценка» не находится ни в одном из диапазонов? –

+1

@ Иордан, это кажется немного суровым, но прискорбно для того, чтобы размножаться, что-то редко встречающееся в этих частях. –

4

Поскольку диапазоны не перекрываются и легко покрыть 0..100 - вам не нужны четкие диапазоны, а что-то вроде:

score_levels = [ 
    {id:1, name: 'Beginner', max_score:50}, 
    {id:2, name: 'Intermediate', max_score:70}, 
    {id:3, name: 'Pro', max_score:85}, 
    {id:4, name: 'Expert', max_score:96}, 
    {id:5, name: 'Master', max_score:100} 
].sort_by{|v| v[:max_score]} 

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

и найти себя (предполагается, что оценка не превышает максимум и всегда находится)

level_counters[ score_levels.find{|v| score <= v[:max_score]}[:id] ] += 1 
+0

Обратите внимание, что OP может потребовать массив в данной форме, чтобы иметь легкий доступ к обоим концам каждого диапазона, поэтому его изменение к тому, что вы предлагаете, может быть менее удобным. Кроме того, ваше решение не столь надежное, как те, которые используют «range.include?». Предположим, например, что оценка была 'mm', а диапазоны были' ('a' ... 'z') 'и' ('aa' ... 'zz') '. Второй диапазон включает 'mm', но вы бы выбрали первый, так как' mm' <= 'z' #=> true'. –

+0

@CarySwoveland, спасибо, конечно, я имел в виду '<=', просто опечатка – Vasfed

+0

да, это решение не так универсально, но должно быть более эффективным, потому что сравнение fixnum намного дешевле – Vasfed

0

Если вы будете смотреть на уровни повторно и должны делать это эффективно, рассмотрим построение отдельной хэш:

score_to_level = score_levels.each_with_object({}) { |(k,v),h| 
    v['range'].each { |v| h[v] = k} } 
    #=> {0=>1, 1=>1, 2=>1, 3=>1, 4=>1, 5=>1, 6=>1, 7=>1, 8=>1, 9=>1, 
    # 10=>1,..., 91=>4, 
    # 92=>4, 93=>4, 94=>4, 95=>4, 96=>4, 97=>5, 98=>5, 99=>5, 100=>5} 

Это, конечно, предполагает, каждый диапазон содержит конечное число значений (не (1.1..3.3) , например).

Смежные вопросы