diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..aa7da42 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,54 @@ +PATH + remote: . + specs: + numbers_in_words (0.3.1) + activesupport + +GEM + remote: https://rubygems.org/ + specs: + activesupport (4.2.5) + i18n (~> 0.7) + json (~> 1.7, >= 1.7.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + codeclimate-test-reporter (0.4.8) + simplecov (>= 0.7.1, < 1.0.0) + diff-lcs (1.2.5) + docile (1.1.5) + i18n (0.7.0) + json (1.8.3) + minitest (5.8.3) + rspec (3.4.0) + rspec-core (~> 3.4.0) + rspec-expectations (~> 3.4.0) + rspec-mocks (~> 3.4.0) + rspec-core (3.4.1) + rspec-support (~> 3.4.0) + rspec-expectations (3.4.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.4.0) + rspec-mocks (3.4.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.4.0) + rspec-support (3.4.1) + simplecov (0.11.0) + docile (~> 1.1.0) + json (~> 1.8) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.0) + thread_safe (0.3.5) + tzinfo (1.2.2) + thread_safe (~> 0.1) + +PLATFORMS + ruby + +DEPENDENCIES + codeclimate-test-reporter + numbers_in_words! + rspec (~> 3.4.0) + +BUNDLED WITH + 1.10.6 diff --git a/lib/numbers_in_words.rb b/lib/numbers_in_words.rb index e004d3b..8dcc85f 100644 --- a/lib/numbers_in_words.rb +++ b/lib/numbers_in_words.rb @@ -21,12 +21,12 @@ def language @language ||= "English" end - def in_words(i, language=NumbersInWords.language) - NumbersInWords::ToWord.new(i, language).in_words + def in_words(i, language=NumbersInWords.language, only_compress=false) + NumbersInWords::ToWord.new(i, language).in_words(only_compress) end - def in_numbers(s, language=NumbersInWords.language) - NumbersInWords::ToNumber.new(s, language).in_numbers + def in_numbers(s, language=NumbersInWords.language, only_compress=false) + NumbersInWords::ToNumber.new(s, language).in_numbers(only_compress) end end end diff --git a/lib/numbers_in_words/duck_punch.rb b/lib/numbers_in_words/duck_punch.rb index 3f6d28d..34f426e 100644 --- a/lib/numbers_in_words/duck_punch.rb +++ b/lib/numbers_in_words/duck_punch.rb @@ -5,8 +5,8 @@ def in_words language=NumbersInWords.language end module WordsInNumbers - def in_numbers language=NumbersInWords.language - NumbersInWords.in_numbers(self, language) + def in_numbers(only_compress = false, language=NumbersInWords.language) + NumbersInWords::ToNumber.new(self, language).in_numbers(only_compress) end end diff --git a/lib/numbers_in_words/number_parser.rb b/lib/numbers_in_words/number_parser.rb index 17ae195..1aedd4c 100644 --- a/lib/numbers_in_words/number_parser.rb +++ b/lib/numbers_in_words/number_parser.rb @@ -39,7 +39,14 @@ module NumbersInWords::NumberParser #3. add memory to answer,reset, because power of ten>2 0 2000 #4. add 1 to memory 1 2000 #5. finish - add memory to answer 0 2001 - def parse(integers) + def parse(integers, only_compress = false) + scales_n = [100, 1000, 1000000, 1000000000, 1000000000000, 10**100] + if integers.length < 2 + return only_compress ? integers : integers.empty? ? 0 : integers[0] + end + if [] == scales_n & integers + return pair_parse(integers, only_compress) + end memory = 0 answer = 0 reset = true #reset each time memory is reset @@ -58,8 +65,7 @@ def parse(integers) reset = true end end - - if memory < integer + if memory < integer memory *= integer else memory += integer @@ -77,5 +83,35 @@ def power_of_ten? integer power_of_ten(integer) == power_of_ten(integer).to_i end + # 15,16 + # 85,16 + def pair_parse(ints, only_compress = false) + ints = compress(ints) + return ints if only_compress + return ints[0] if ints.length == 1 + sum = 0 + ints.each do |n| + sum *= n >= 10 ? 100 : 10 + sum += n + end + sum + end + + # [40, 2] => [42] + def compress(ints) + res = []; i = 0 + return [] if ints.empty? + while i < ints.length - 1 + if ints[i] > 0 && ints[i] % 10 == 0 && ints[i + 1] < 10 + res << ints[i] + ints[i + 1] + i += 2 + else + res << ints[i] + i += 1 + end + end + i < ints.length ? res << ints[-1] : res + end + extend self end diff --git a/lib/numbers_in_words/to_number.rb b/lib/numbers_in_words/to_number.rb index 68dec3a..d637ebf 100644 --- a/lib/numbers_in_words/to_number.rb +++ b/lib/numbers_in_words/to_number.rb @@ -16,27 +16,48 @@ def language end end - def handle_negative text - -1 * (text.gsub(/^minus /, "")).in_numbers if text =~ /^minus / + def handle_negative(text, only_compress) + if text =~ /^minus/ + stripped = text.gsub(/^minus/, "") + stripped_n = NumbersInWords.in_numbers(stripped, language, only_compress) + only_compress ? stripped_n.map{ |k| k * -1 } : -1 * stripped_n + end end - def in_numbers - text = to_s + def in_numbers(only_compress = false) + text = to_s.strip + return text.to_f if text =~ /^-?\d+(.\d+)?$/ text = strip_punctuation text - i = handle_negative text + + i = handle_negative(text, only_compress) return i if i + mixed = text.match /^(-?\d+(.\d+)?) (hundred|thousand|million|billion|trillion)$/ + + if mixed && mixed[1] && mixed[3] + third_match = NumbersInWords.in_numbers(mixed[3]) + first_match = NumbersInWords.in_numbers(mixed[1]) + return first_match * third_match + end + + one = text.match /^one (hundred|thousand|million|billion|trillion)$/ + + if one + first_match = NumbersInWords.in_numbers(one[1]) + return only_compress ? [first_match] : first_match + end + h = handle_decimals text return h if h integers = word_array_to_integers text.split(" ") - NumbersInWords::NumberParser.parse integers + NumbersInWords::NumberParser.parse integers, only_compress end def strip_punctuation text - text = text.downcase.gsub(/[^a-z ]/, " ") + text = text.downcase.gsub(/[^a-z 0-9]/, " ") to_remove = true to_remove = text.gsub! " ", " " while to_remove @@ -47,22 +68,12 @@ def strip_punctuation text def handle_decimals text match = text.match(/\spoint\s/) if match - integer = match.pre_match.in_numbers - - decimal = decimal_portion match.post_match - - integer + decimal + integer = NumbersInWords.in_numbers(match.pre_match) + decimal = NumbersInWords.in_numbers(match.post_match) + integer += "0.#{decimal}".to_f end end - - def decimal_portion text - words = text.split " " - integers = word_array_to_integers words - decimal = "0." + integers.join() - decimal.to_f - end - #handles simple single word numbers #e.g. one, seven, twenty, eight, thousand etc def word_to_integer word diff --git a/lib/numbers_in_words/version.rb b/lib/numbers_in_words/version.rb index 7b50ca1..fd8ea92 100644 --- a/lib/numbers_in_words/version.rb +++ b/lib/numbers_in_words/version.rb @@ -1,3 +1,3 @@ module NumbersInWords - VERSION = "0.3.0" + VERSION = "0.3.1" end diff --git a/numbers_in_words.gemspec b/numbers_in_words.gemspec index f5018aa..539a101 100644 --- a/numbers_in_words.gemspec +++ b/numbers_in_words.gemspec @@ -9,9 +9,9 @@ Gem::Specification.new do |gem| gem.summary = "Example: 123.in_words # => \"one hundred and twenty three\", \"seventy-five point eight\".in_numbers # = > 75.8" gem.version = NumbersInWords::VERSION - gem.authors = ["Mark Burns"] - gem.email = ["markthedeveloper@gmail.com"] - gem.homepage = "http://github.com/markburns/numbers_in_words" + gem.authors = ["Mark Burns", "Dimid Duchovny"] + gem.email = ["dimidd@gmail.com"] + gem.homepage = "http://github.com/dimidd/numbers_in_words" gem.add_dependency "activesupport" gem.add_development_dependency "rspec", "~> 3.4.0" diff --git a/spec/non_monkey_patch_spec.rb b/spec/non_monkey_patch_spec.rb index 5af790d..7b433dc 100644 --- a/spec/non_monkey_patch_spec.rb +++ b/spec/non_monkey_patch_spec.rb @@ -19,6 +19,12 @@ describe ".in_numbers" do it do expect(NumbersInWords.in_numbers("one hundred")).to eq 100 + + expect(NumbersInWords.in_numbers("minus one hundred")).to eq -100 + expect(NumbersInWords.in_numbers("twenty four" )).to eq 24 + expect(NumbersInWords.in_numbers("one point two")).to eq 1.2 + expect(NumbersInWords.in_numbers("one hundred googol")).to eq 100*10**100 + expect(NumbersInWords.in_numbers("one hundred googol and thirty")).to eq 30 + 100*10**100 end end diff --git a/spec/numerical_strings_spec.rb b/spec/numerical_strings_spec.rb new file mode 100644 index 0000000..5adc20f --- /dev/null +++ b/spec/numerical_strings_spec.rb @@ -0,0 +1,19 @@ +require './spec/spec_helper' + +describe NumbersInWords do + it "should recognize numerical strings" do + arr = %w(8 56 100 5789 3435356) + arr.each{ |s| expect(s.in_numbers).to eql(s.to_f) } + end + + it "should recognize mixed strings" do + mixed = { + "19 hundred" => 1_900.0, + "20 thousand" => 20_000.0, + "100 million" => 100_000_000.0, + "7 billion" => 7_000_000_000.0, + "42 trillion" => 42_000_000_000_000.0 + } + mixed.each{ |k, v| expect(k.in_numbers).to eql(v) } + end +end diff --git a/spec/words_in_numbers_spec.rb b/spec/words_in_numbers_spec.rb index 8a13d55..ddea76c 100644 --- a/spec/words_in_numbers_spec.rb +++ b/spec/words_in_numbers_spec.rb @@ -132,4 +132,14 @@ in_numbers).to eq(75.84327694518) end + it "should handle years notation" do + expect("fifteen sixteen".in_numbers) .to eq(1516) + expect("eighty five sixteen".in_numbers) .to eq(8516) + expect("nineteen ninety six".in_numbers) .to eq(1996) + expect("forty nine ninety eight forty seven seventy nine".in_numbers) .to eq(49984779) + expect("fifteen sixteen".in_numbers) .to eq(1516) + expect("fifteen sixteen seven".in_numbers) .to eq(15167) + expect("fifteen sixteen seventeen".in_numbers) .to eq(151617) + end + end diff --git a/spec/years_spec.rb b/spec/years_spec.rb new file mode 100644 index 0000000..2be67ea --- /dev/null +++ b/spec/years_spec.rb @@ -0,0 +1,16 @@ +require './spec/spec_helper' + +describe WordsInNumbers do + it "should handle years notation" do + expect("fifteen sixteen seventeen".in_numbers) .to eq(151617) + expect("forty nine ninety eight forty seven seventy nine".in_numbers) .to eq(49984779) + expect("sixty seven six".in_numbers) .to eq(676) + expect("one fifty".in_numbers).to eq(150) + expect("two fifty".in_numbers).to eq(250) + expect("one point fifty six fifty seven".in_numbers).to eq(1.5657) + expect("one three forty seven".in_numbers).to eq(1347) + expect("one three five point forty seven".in_numbers).to eq(135.47) + expect("one ten sixty three".in_numbers).to eq(11063) + end + +end