validation on discount between 0 and 0.999 in laravel 5 - laravel

I need to apply validation on discount filed accept value between 0 and 0.999
like: 0.25, 0.0125, 0.09
I tried
'discount' => 'required|max:0.999'
but got: The discount may not be greater than 0.999 characters.

The max rule looks at the type of the variable being sent to it and applies the appropriate logic. For numbers, it works like you intend - it compares the numerical value.
But for strings, it means that the string may not be longer than the max. In your case, Laravel thinks you're sending a string and tries to validate it as such. Your variables probably aren't 0.25, 0.5, etc., but rather "0.25", "0.5", etc. If you convert them to floats, it should work fine.
If, for instance, your values come directly from forms, they're most likely in string form, not float.

Size is what you need here
The field under validation must have a size matching the given value.
For string data, value corresponds to the number of characters. For
numeric data, value corresponds to a given integer value. For files,
size corresponds to the file size in kilobytes.
'discount' => 'required|size:0.999'

'discount' => 'required|numeric|max:0.999'

Related

How to read percentage as decimal number?

I'm trying to find the decimal value from a percentage that a user inputs.
For example, if a user inputs "15", i will need to do a calculation of 0.15 * number.
I've tried using .to_f, but it returns 15.0:
15.to_f
#=> 15.0
I also tried to add 0. to the beginning of the percentage, but it just returns 0:
15.to_s.rjust(4, "0.").to_i
#=> 0
Divide by 100.0
The easiest way to do what you're trying to do is to divide your input value by a Float (keeping in mind the inherent inaccuracy of floating point values). For example:
percentage = 15
percentage / 100.0
#=> 0.15
One benefit of this approach (among others) is that it can handle fractional percentages just as easily. Consider:
percentage = 15.6
percentage / 100.0
#=> 0.156
If floating point precision isn't sufficient for your use case, then you should consider using Rational or BigDecimal numbers instead of a Float. Your mileage will very much depend on your semantic intent and accuracy requirements.
Caveats
Make sure you have ahold of a valid Integer in the first place. While others might steer you towards String#to_i, a more robust validation is to use Kernel#Integer so that an exception will be raised if the value can't be coerced into a valid Integer. For example:
print "Enter integer: "
percentage = Integer gets
If you enter 15\n then:
percentage.class
#=> Integer
If you enter something that can't be coerced to an Integer, like foo\n, then:
ArgumentError (invalid value for Integer(): "foo\n")
Using String#to_i is much more permissive, and can return 0 when you aren't expecting it, such as when called on nil, an empty string, or alphanumeric values that don't start with an integer. It has other interesting edge cases as well, so it's not always the best option for validating input.
I'm trying to find the amount from a percentage that a user inputs
If you retrieve the input via gets, you typically convert it to a numeric value first, e.g.
percentage = gets.to_i
#=> 15
Ruby is not aware that this 15 is a percentage. And since there's no Percentage class, you have to convert it into one of the existing numeric classes.
15% is equal to the fraction 15/100, the ratio 15:100, or the decimal number 0.15.
If you want the number as a (maybe inexact) Float, you can divide it by 100 via fdiv:
15.fdiv(100)
#=> 0.15
If you prefer a Rational you can use quo: (it might also return an Integer)
15.quo(100)
#=> (3/20)
Or maybe BigDecimal for an arbitrary-precision decimal number:
require 'bigdecimal'
BigDecimal(15) / 100
#=> 0.15e0
BigDecimal also accepts strings, so you could pass the input without prior conversion:
input = gets
BigDecimal(input) / 100
#=> 0.15e0

How do I get Django Rest Framework to round decimals to the maximum precision?

I have a Django Rest Framework serializer with a DecimalField
serializers.DecimalField(max_digits=9, decimal_places=6)
Now if I try to deserialize data that contains a decimal with a higher precision (i.e. 50.1234567) the serializer raises a ValidationError:
"Ensure that there are no more than 6 decimal places."
This even happens if the last digit is 0. Is it possible to make the serializer round the given value to the maximum precision (i.e. 50.1234567 to 50.123457)? And if so how?
After coercing the input to a Decimal the DecimalField validates the precision of the value in the aptly named, but undocumented, validate_precision method. So to disable this validation one can override this method and simply return the input value:
class RoundingDecimalField(serializers.DecimalField):
def validate_precision(self, value):
return value
It turns out that doing this is enough to get the desired rounding behaviour.
After calling validate_precision the DecimalField calls quantize which will "Quantize the decimal value to the configured precision" (from the docstring).
The rounding mode for this quantisation process is controlled by the current active decimal context.
If a specific rounding mode is desired one can use the (again undocumented) drf_braces.fields.custom.RoundedDecimalField field from django-rest-framework-braces. This field takes an optional rounding argument where one can specify the desired rounding mode.
Appreciate the answer, #jaap3. Wanted to add my implementation here for reference for others who find this question. Here's how I used this rounding field inside another serializer class with an attribute I wanted rounded to the max_digits value set on the Position model.
class RoundingDecimalField(serializers.DecimalField):
"""Used to automaticaly round decimals to the model's accepted value."""
def validate_precision(self, value):
return value
class PositionSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='target_position-detail')
price = RoundingDecimalField(max_digits=21, decimal_places=14)
In order to avoid losing the default implementation of validate_precision, as pointed out by #robvdl, I adopted the follwoing solution:
class RoundingDecimalField(serializers.DecimalField):
def validate_precision(self, value):
# This is needed to avoid to raise an error if `value` has more decimals than self.decimal_places.
with decimal.localcontext() as ctx:
if self.rounding:
ctx.rounding = self.rounding
value = round(value, self.decimal_places)
return super().validate_precision(value)

Float with 'unnecessary' digits

I need to make a number out of the string. to do that i use well known maneuver looking something like this:
Float(string_var) rescue nil
This works nicely, however I do have a tiny, little problem. If a string is "2.50", variable I get is 2.5. Is it even possible to create Float object with 'unnecessary' 0 digit at the end? can I literally translate "2.50" into 2.50 ?
In short, the answer is no, given the question, as any Float, when examined, will use Float's to_s function, eliciting an answer without trailing zeroes.
Float will always give you a numeric value that can be interpreted any way you wish, though. In your example, you will get a float value (given a string that is a parsable float). What you are asking then, is how to display that value with trailing zeroes. To do that, you will be turning the float value back into a string.
Easiest way to accomplish that is to use the format given by one of your respondents, namely
string_var = "2.50"
float_value = Float(string_var) rescue nil # 2.5
with_trailing_zeroes = "%0.2f" % float_value # '2.50'

string.to_f precision issue when the value trailed by two zeros

I get input string "400.00" and need to display 400.00.
Expected float_value :400.00
I used to_f to do the task since to_i will return 400 alone. The code is
x="400.00"
float_value=x.to_f
But in this case, I am getting the output as 400.0 which is not acceptable for my case.
current float_value :400.0
Both are equal and have no difference, but its not for any calculation purpose, its for some other display purpose.
Use sprintf for fomatting:
sprintf("%.2f", 400)
#=> "400.00"

Generating integer within range from unique string in ruby

I have a code that should get unique string(for example, "d86c52ec8b7e8a2ea315109627888fe6228d") from client and return integer more than 2200000000 and less than 5800000000. It's important, that this generated int is not random, it should be one for one unique string. What is the best way to generate it without using DB?
Now it looks like this:
did = "d86c52ec8b7e8a2ea315109627888fe6228d"
min_cid = 2200000000
max_cid = 5800000000
cid = did.hash.abs.to_s.split.last(10).to_s.to_i
if cid < min_cid
cid += min_cid
else
while cid > max_cid
cid -= 1000000000
end
end
Here's the problem - your range of numbers has only 3.6x10^9 possible values where as your sample unique string (which looks like a hex integer with 36 digits) has 16^32 possible values (i.e. many more). So when mapping your string into your integer range there will be collisions.
The mapping function itself can be pretty straightforward, I would do something such as below (also, consider using only a part of the input string for integer conversion, e.g. the first seven digits, if performance becomes critical):
def my_hash(str, min, max)
range = (max - min).abs
(str.to_i(16) % range) + min
end
my_hash(did, min_cid, max_cid) # => 2461595789
[Edit] If you are using Ruby 1.8 and your adjusted range can be represented as a Fixnum, just use the hash value of the input string object instead of parsing it as a big integer. Note that this strategy might not be safe in Ruby 1.9 (per the comment by #DataWraith) as object hash values may be randomized between invocations of the interpreter so you would not get the same hash number for the same input string when you restart your application:
def hash_range(obj, min, max)
(obj.hash % (max-min).abs) + [min, max].min
end
hash_range(did, min_cid, max_cid) # => 3886226395
And, of course, you'll have to decide what to do about collisions. You'll likely have to persist a bucket of input strings which map to the same value and decide how to resolve the conflicts if you are looking up by the mapped value.
You could generate a 32-bit CRC, drop one bit, and add the result to 2.2M. That gives you a max value of 4.3M.
Alternately you could use all 32 bits of the CRC, but when the result is too large, append a zero to the input string and recalculate, repeating until you get a value in range.

Resources