Nick M
Nick M

Reputation: 2532

Rounding numbers in ruby up or down to nearest 10 / 100 / 1000 in a different way

Short version: a different way to "round" numbers to user-friendly values without .round/.floor/.ceil.

Long boring version: Imagine the following conversations between a salesperson and clients:

Salesperson: how much would you pay for product X? Small timer: $15 SP: I can sell it to you at $50 ST: OK, I can live with that.

Had the salesperson said $100 the small timer would have said nah can’t afford it. And obviously he can’t just give it away for free because the salesperson isn’t a charity.

Then again with an average guy:

Salesperson: how much would you pay for product X? Average guy: don’t know...$120? SP: nah $200 is the minimum we’d sell for. AG: ok fine.

Then the big daddy comes in:

Salesperson: how much would you like to spend today sir? Big daddy: about $1200? SP: why not $1500? Big daddy: yeah let’s do $1500. SP: why not $1650? Big daddy: we might as well do $2000. SP: thank you for your business sir!

Yes, we do know who’s who, as in about how much money the customer may have, eg for a big guy we know he has around $2000-$5000 on him.

So I am looking for a way to round product prices in a smart but sensitive way as follows:

15 -> 50 #!
23 -> 50
51 -> 100
71 -> 100
109 -> 100
132 -> 150 
124 -> 150 #!
173 -> 200
399 -> 400
549 -> 500
1231 -> 1500 #!
2761 -> 3000 
3104 -> 3000
3249 -> 3500 #!

Basically round the prices up or down to make them user-friendly but without deviating that much from common sense...

class Price

    def initialize(s = 0) # possibly add customer “size”?
        @price = s
    end

    # operations
    def inc_to(x)
        @price = [@price, x].flatten.max
        self
    end

    def dec_to(x)
        @price = [@price, x].flatten.min
        self
    end

    def inc_with(x)
        @price+=x
        self
    end

    def dec_by(x)
        @price = @price - x
        self
    end

    def avg(x)
        arr = [@price, x]
        @price = arr.inject{ |sum, el| sum + el }.to_f / arr.size
        self
    end

    def round
        #@price = ?
        self
    end

    # getters
    def value
        @price
    end

    def to_i
        @price.to_i
    end

    def to_f
        @price.to_f
    end

end

I've tried writing this example class but can't seem to pull a nice .round method, will appreciate any input.

Price.new(15).inc_to(1000).dec_to(700).avg(100).inc_to(200).inc_to(400).dec_to(351).inc_with(48).value # .round.to_i

Upvotes: 0

Views: 598

Answers (1)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121010

It’s not possible to handle the input as you want with the generic rounding method because of contradictory rules (you want 51 to be “rounded” to 100 and others floored down.)

I would go with producing a hash or Range → Price pairs:

rounds = {
  (0..50) => 50,
  (51..110) => 100,
  (111..165) => 150,
  (166..220) => 200,
  ...
  (700..1200) => 1000,
  (1201..1600) => 1500,
  ...
  (4200..7000) => 5000      
}

and then just do detect:

rounds.detect { |range, _| range === price }.last

Upvotes: 2

Related Questions