numbers_in_words 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8ae79a47b168a7aa57ce40a4cea7cfc6c35eae27
4
- data.tar.gz: dab916e39bd24eeaff4c02c06816874506b053cb
3
+ metadata.gz: f2857f0873d094b51ce7ca5e4a90ac8e999bebb6
4
+ data.tar.gz: b0e885874e36c119b69290ae0bb30110419f7031
5
5
  SHA512:
6
- metadata.gz: c6ae5c2eb9cad7c67563c2cad679741098841c7d74b12ac0dfa45a50082baedb511c3bb58b1a92f887bdb5857d9189c048addecdaa387a47cbe76f58fab26816
7
- data.tar.gz: 76d00e6c848827df1739a9d0b0d3476b061c5355a5d2fb13eab0d2a432c1d9c061a133b76d17286690d0408879e9857085d86ce86c31243038f2776f7e2db5d1
6
+ metadata.gz: 98b44efc9e3c5bd28d5f3f26d11fd4b62f12fde010ce4ca30fe35e57ca10a8cb0822ac50fcb977a4823d7c722a75c71c670b52584c5361c2aae8fe2527ff95a9
7
+ data.tar.gz: 99b23e8c07dea648e1afc393f28f4b8981594c30a05bb4970b616cd76098294ddaf7b450bf683dd1656e8cadf01573b7fcf86164e32d12afbba95adde26e31ed
data/.codeclimate.yml CHANGED
@@ -10,3 +10,5 @@ ratings:
10
10
  - "**.rb"
11
11
  exclude_paths:
12
12
  - spec/**/*
13
+ - .codeclimate.yml
14
+ - .rubocop.yml
data/README.md CHANGED
@@ -1,9 +1,10 @@
1
1
  [![Build Status](http://img.shields.io/travis/markburns/numbers_in_words.svg)](https://travis-ci.org/markburns/numbers_in_words)
2
2
  [![Dependency Status](http://img.shields.io/gemnasium/markburns/numbers_in_words.svg)](https://gemnasium.com/markburns/numbers_in_words)
3
3
  [![Code Climate](http://img.shields.io/codeclimate/github/markburns/numbers_in_words.svg)](https://codeclimate.com/github/markburns/numbers_in_words)
4
+ [![Test Coverage](https://codeclimate.com/github/markburns/numbers_in_words/badges/coverage.svg)](https://codeclimate.com/github/markburns/numbers_in_words/coverage)
4
5
  [![Gem Version](http://img.shields.io/gem/v/numbers_in_words.svg)](https://rubygems.org/gems/numbers_in_words)
5
6
  [![License](http://img.shields.io/:license-mit-blue.svg)](http://markburns.mit-license.org)
6
- [![Badges](http://img.shields.io/:badges-6/6-ff6799.svg)](https://github.com/badges/badgerbadgerbadger)
7
+ [![Badges](http://img.shields.io/:badges-7/7-ff6799.svg)](https://github.com/badges/badgerbadgerbadger)
7
8
 
8
9
  Installation
9
10
  ============
@@ -12,7 +13,7 @@ Installation
12
13
  gem 'numbers_in_words'
13
14
 
14
15
  require 'numbers_in_words'
15
- require 'numbers_in_words/duck_punch' #see why later
16
+ require 'numbers_in_words/duck_punch' #optional see why later
16
17
  ```
17
18
 
18
19
  This project was created for a test for a job interview. I haven't really used
@@ -27,7 +28,7 @@ require 'numbers_in_words'
27
28
  NumbersInWords.in_words(112)
28
29
  #=> one hundred and twelve
29
30
 
30
- NumbersInWords.in_numbers(112)
31
+ NumbersInWords.in_numbers("one googol")
31
32
  #=>10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
32
33
 
33
34
  NumbersInWords.in_numbers("Seventy million, five-hundred and fifty six thousand point eight nine three")
data/bin/spec CHANGED
File without changes
@@ -21,12 +21,12 @@ module NumbersInWords
21
21
  @language ||= "English"
22
22
  end
23
23
 
24
- def in_words(i, language=NumbersInWords.language)
25
- NumbersInWords::ToWord.new(i, language).in_words
24
+ def in_words(i, language=NumbersInWords.language, only_compress=false)
25
+ NumbersInWords::ToWord.new(i, language).in_words(only_compress)
26
26
  end
27
27
 
28
- def in_numbers(s, language=NumbersInWords.language)
29
- NumbersInWords::ToNumber.new(s, language).in_numbers
28
+ def in_numbers(s, language=NumbersInWords.language, only_compress=false)
29
+ NumbersInWords::ToNumber.new(s, language).in_numbers(only_compress)
30
30
  end
31
31
  end
32
32
  end
@@ -5,8 +5,8 @@ module NumbersInWords
5
5
  end
6
6
 
7
7
  module WordsInNumbers
8
- def in_numbers language=NumbersInWords.language
9
- NumbersInWords.in_numbers(self, language)
8
+ def in_numbers(only_compress = false, language=NumbersInWords.language)
9
+ NumbersInWords::ToNumber.new(self, language).in_numbers(only_compress)
10
10
  end
11
11
  end
12
12
 
@@ -1,5 +1,14 @@
1
1
  module NumbersInWords
2
2
  module English
3
+
4
+ def self.canonize(w)
5
+ aliases = {
6
+ "oh" => "zero"
7
+ }
8
+ canon = aliases[w]
9
+ return canon ? canon : w
10
+ end
11
+
3
12
  def self.exceptions
4
13
  {
5
14
  0 => "zero",
@@ -89,5 +98,27 @@ module NumbersInWords
89
98
  def self.powers_of_ten_to_i
90
99
  swap_keys powers_of_ten
91
100
  end
101
+
102
+ POWERS_RX = Regexp.union(powers_of_ten.values[1..-1])
103
+
104
+ def self.check_mixed(txt)
105
+ mixed = txt.match /^(-?\d+(.\d+)?) (#{POWERS_RX}s?)$/
106
+ if mixed && mixed[1] && mixed[3]
107
+ matches = [mixed[1], mixed[3]].map{ |m| NumbersInWords.in_numbers m }
108
+ return matches.reduce(&:*)
109
+ end
110
+ end
111
+
112
+ def self.check_one(txt)
113
+ one = txt.match /^one (#{POWERS_RX})$/
114
+ end
115
+
116
+ def self.strip_minus(txt)
117
+ stripped = txt.gsub(/^minus/, "") if txt =~ /^minus/
118
+ end
119
+
120
+ def self.check_decimal(txt)
121
+ txt.match(/\spoint\s/)
122
+ end
92
123
  end
93
124
  end
@@ -23,22 +23,29 @@ module NumbersInWords
23
23
 
24
24
  return negative() if number < 0
25
25
 
26
- length = number.to_s.length
26
+ output = if number.to_s.length == 2 #20-99
27
+ handle_tens(number)
28
+ else
29
+ write() #longer numbers
30
+ end
31
+
32
+ output.strip
33
+ end
34
+
35
+ def handle_tens(number)
27
36
  output = ""
28
37
 
29
- if length == 2 #20-99
30
- tens = (number/10).round*10 #write the tens
38
+ tens = (number/10).round*10 #write the tens
31
39
 
32
- output << exceptions[tens] # e.g. eighty
40
+ output << exceptions[tens] # e.g. eighty
33
41
 
34
- digit = number - tens #write the digits
42
+ digit = number - tens #write the digits
35
43
 
36
- output << " " + NumbersInWords.in_words(digit) unless digit==0
37
- else
38
- output << write() #longer numbers
44
+ unless digit == 0
45
+ output << " " + NumbersInWords.in_words(digit)
39
46
  end
40
47
 
41
- output.strip
48
+ output
42
49
  end
43
50
 
44
51
  def handle_exception
@@ -15,7 +15,6 @@ module NumbersInWords
15
15
  end
16
16
  end
17
17
 
18
-
19
18
  def group_words size
20
19
  #1000 and over Numbers are split into groups of three
21
20
  groups = NumberGroup.groups_of @that, size
@@ -3,8 +3,8 @@ module NumbersInWords
3
3
  include Enumerable
4
4
  attr_accessor :number
5
5
 
6
- def each
7
- @array.each { |item| yield item}
6
+ def self.groups_of number, size
7
+ new(number).groups(size)
8
8
  end
9
9
 
10
10
  def initialize number
@@ -42,10 +42,6 @@ module NumbersInWords
42
42
  end
43
43
  end
44
44
 
45
- def self.groups_of number, size
46
- new(number).groups(size)
47
- end
48
-
49
45
  def split_googols
50
46
  googols = @number.to_s[0 .. (-LENGTH_OF_GOOGOL)].to_i
51
47
  remainder = @number.to_s[(1-LENGTH_OF_GOOGOL) .. -1].to_i
@@ -1,4 +1,5 @@
1
1
  module NumbersInWords::NumberParser
2
+ SCALES_N = [100, 1000, 1000000, 1000000000, 1000000000000, 10**100]
2
3
  # Example: 364,895,457,898
3
4
  #three hundred and sixty four billion eight hundred and ninety five million
4
5
  #four hundred and fifty seven thousand eight hundred and ninety eight
@@ -39,7 +40,7 @@ module NumbersInWords::NumberParser
39
40
  #3. add memory to answer,reset, because power of ten>2 0 2000
40
41
  #4. add 1 to memory 1 2000
41
42
  #5. finish - add memory to answer 0 2001
42
- def parse(integers)
43
+ def parse_ints(integers)
43
44
  memory = 0
44
45
  answer = 0
45
46
  reset = true #reset each time memory is reset
@@ -58,8 +59,7 @@ module NumbersInWords::NumberParser
58
59
  reset = true
59
60
  end
60
61
  end
61
-
62
- if memory < integer
62
+ if memory < integer
63
63
  memory *= integer
64
64
  else
65
65
  memory += integer
@@ -69,6 +69,18 @@ module NumbersInWords::NumberParser
69
69
  answer += memory
70
70
  end
71
71
 
72
+ def parse(integers, only_compress = false)
73
+ if integers.length < 2
74
+ return integers if only_compress
75
+ return integers.empty? ? 0 : integers[0]
76
+ end
77
+ if [] == (SCALES_N & integers)
78
+ return pair_parse(integers, only_compress)
79
+ end
80
+
81
+ parse_ints(integers)
82
+ end
83
+
72
84
  def power_of_ten integer
73
85
  Math.log10(integer)
74
86
  end
@@ -77,5 +89,47 @@ module NumbersInWords::NumberParser
77
89
  power_of_ten(integer) == power_of_ten(integer).to_i
78
90
  end
79
91
 
92
+ # 15,16
93
+ # 85,16
94
+ def pair_parse(ints, only_compress = false)
95
+ ints = compress(ints)
96
+ return ints if only_compress
97
+ return ints[0] if ints.length == 1
98
+ sum = 0
99
+ ints.each do |n|
100
+ sum *= n >= 10 ? 100 : 10
101
+ sum += n
102
+ end
103
+ sum
104
+ end
105
+
106
+ # [40, 2] => [42]
107
+ def compress(ints)
108
+ res = []
109
+ i = 0
110
+ return [] if ints.empty?
111
+ while i < ints.length - 1
112
+ int, jump = compress_int(ints[i], ints[i + 1])
113
+ res << int
114
+ i += jump
115
+ end
116
+ if i < ints.length
117
+ res << ints[-1]
118
+ else
119
+ res
120
+ end
121
+ end
122
+
123
+ def compress_int(int, sequel)
124
+ tens = int % 10 == 0 && int > 10
125
+ if tens && sequel < 10
126
+ return [int + sequel, 2]
127
+ else
128
+ return [int, 1]
129
+ end
130
+
131
+ [res, jump]
132
+ end
133
+
80
134
  extend self
81
135
  end
@@ -1,6 +1,7 @@
1
1
  class NumbersInWords::ToNumber
2
2
  delegate :to_s, to: :that
3
- delegate :powers_of_ten_to_i, :exceptions_to_i, to: :language
3
+ delegate :powers_of_ten_to_i, :exceptions_to_i, :canonize, \
4
+ :check_mixed, :check_one, :strip_minus, :check_decimal, to: :language
4
5
  attr_reader :that, :language
5
6
 
6
7
  def initialize that, language=NumbersInWords.language
@@ -16,27 +17,42 @@ class NumbersInWords::ToNumber
16
17
  end
17
18
  end
18
19
 
19
- def handle_negative text
20
- -1 * (text.gsub(/^minus /, "")).in_numbers if text =~ /^minus /
20
+ def handle_negative(text, only_compress)
21
+ stripped = strip_minus text
22
+ if stripped
23
+ stripped_n = NumbersInWords.in_numbers(stripped, language, only_compress)
24
+ only_compress ? stripped_n.map{ |k| k * -1 } : -1 * stripped_n
25
+ end
21
26
  end
22
27
 
23
- def in_numbers
24
- text = to_s
28
+ def in_numbers(only_compress = false)
29
+ text = to_s.strip
30
+ return text.to_f if text =~ /^-?\d+(.\d+)?$/
25
31
 
26
32
  text = strip_punctuation text
27
- i = handle_negative text
33
+
34
+ i = handle_negative(text, only_compress)
28
35
  return i if i
29
36
 
37
+ mixed = check_mixed text
38
+ return mixed if mixed
39
+
40
+ one = check_one text
41
+ if one
42
+ res = NumbersInWords.in_numbers(one[1], language)
43
+ return only_compress ? [res] : res
44
+ end
45
+
30
46
  h = handle_decimals text
31
47
  return h if h
32
48
 
33
49
  integers = word_array_to_integers text.split(" ")
34
50
 
35
- NumbersInWords::NumberParser.parse integers
51
+ NumbersInWords::NumberParser.parse integers, only_compress
36
52
  end
37
53
 
38
54
  def strip_punctuation text
39
- text = text.downcase.gsub(/[^a-z ]/, " ")
55
+ text = text.downcase.gsub(/[^a-z 0-9]/, " ")
40
56
  to_remove = true
41
57
 
42
58
  to_remove = text.gsub! " ", " " while to_remove
@@ -45,28 +61,18 @@ class NumbersInWords::ToNumber
45
61
  end
46
62
 
47
63
  def handle_decimals text
48
- match = text.match(/\spoint\s/)
64
+ match = check_decimal text
49
65
  if match
50
- integer = match.pre_match.in_numbers
51
-
52
- decimal = decimal_portion match.post_match
53
-
54
- integer + decimal
66
+ integer = NumbersInWords.in_numbers(match.pre_match)
67
+ decimal = NumbersInWords.in_numbers(match.post_match)
68
+ integer += "0.#{decimal}".to_f
55
69
  end
56
70
  end
57
71
 
58
-
59
- def decimal_portion text
60
- words = text.split " "
61
- integers = word_array_to_integers words
62
- decimal = "0." + integers.join()
63
- decimal.to_f
64
- end
65
-
66
72
  #handles simple single word numbers
67
73
  #e.g. one, seven, twenty, eight, thousand etc
68
74
  def word_to_integer word
69
- text = word.to_s.chomp.strip
75
+ text = canonize(word.to_s.chomp.strip)
70
76
 
71
77
  exception = exceptions_to_i[text]
72
78
  return exception if exception
@@ -1,3 +1,3 @@
1
1
  module NumbersInWords
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -9,8 +9,8 @@ Gem::Specification.new do |gem|
9
9
  gem.summary = "Example: 123.in_words # => \"one hundred and twenty three\", \"seventy-five point eight\".in_numbers # = > 75.8"
10
10
 
11
11
  gem.version = NumbersInWords::VERSION
12
- gem.authors = ["Mark Burns"]
13
- gem.email = ["markthedeveloper@gmail.com"]
12
+ gem.authors = ["Mark Burns", "Dimid Duchovny"]
13
+ gem.email = ["markthedeveloper@gmail.com", "dimidd@gmail.com"]
14
14
  gem.homepage = "http://github.com/markburns/numbers_in_words"
15
15
 
16
16
  gem.add_dependency "activesupport"
@@ -19,6 +19,12 @@ describe "NumbersInWords" do
19
19
  describe ".in_numbers" do
20
20
  it do
21
21
  expect(NumbersInWords.in_numbers("one hundred")).to eq 100
22
+
23
+ expect(NumbersInWords.in_numbers("minus one hundred")).to eq -100
24
+ expect(NumbersInWords.in_numbers("twenty four" )).to eq 24
25
+ expect(NumbersInWords.in_numbers("one point two")).to eq 1.2
26
+ expect(NumbersInWords.in_numbers("one hundred googol")).to eq 100*10**100
27
+ expect(NumbersInWords.in_numbers("one hundred googol and thirty")).to eq 30 + 100*10**100
22
28
  end
23
29
  end
24
30
 
@@ -0,0 +1,19 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe NumbersInWords do
4
+ it "should recognize numerical strings" do
5
+ arr = %w(8 56 100 5789 3435356)
6
+ arr.each{ |s| expect(s.in_numbers).to eql(s.to_f) }
7
+ end
8
+
9
+ it "should recognize mixed strings" do
10
+ mixed = {
11
+ "19 hundred" => 1_900.0,
12
+ "20 thousand" => 20_000.0,
13
+ "100 million" => 100_000_000.0,
14
+ "7 billion" => 7_000_000_000.0,
15
+ "42 trillion" => 42_000_000_000_000.0
16
+ }
17
+ mixed.each{ |k, v| expect(k.in_numbers).to eql(v) }
18
+ end
19
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,7 @@
1
+ require "codeclimate-test-reporter"
2
+ CodeClimate::TestReporter.start
3
+
1
4
  require 'numbers_in_words'
2
5
  require 'numbers_in_words/duck_punch'
3
6
 
4
- require "codeclimate-test-reporter"
5
- CodeClimate::TestReporter.start
6
7
 
@@ -132,4 +132,14 @@ describe WordsInNumbers do
132
132
  in_numbers).to eq(75.84327694518)
133
133
  end
134
134
 
135
+ it "should handle years notation" do
136
+ expect("fifteen sixteen".in_numbers) .to eq(1516)
137
+ expect("eighty five sixteen".in_numbers) .to eq(8516)
138
+ expect("nineteen ninety six".in_numbers) .to eq(1996)
139
+ expect("forty nine ninety eight forty seven seventy nine".in_numbers) .to eq(49984779)
140
+ expect("fifteen sixteen".in_numbers) .to eq(1516)
141
+ expect("fifteen sixteen seven".in_numbers) .to eq(15167)
142
+ expect("fifteen sixteen seventeen".in_numbers) .to eq(151617)
143
+ end
144
+
135
145
  end
@@ -0,0 +1,17 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe WordsInNumbers do
4
+ it "should handle years notation" do
5
+ expect("fifteen sixteen seventeen".in_numbers).to eq(151617)
6
+ expect("forty nine ninety eight forty seven seventy nine".in_numbers).to eq(49984779)
7
+ expect("sixty seven six".in_numbers) .to eq(676)
8
+ expect("one fifty".in_numbers).to eq(150)
9
+ expect("two fifty".in_numbers).to eq(250)
10
+ expect("one point fifty six fifty seven".in_numbers).to eq(1.5657)
11
+ expect("one three forty seven".in_numbers).to eq(1347)
12
+ expect("one three five point forty seven".in_numbers).to eq(135.47)
13
+ expect("one ten sixty three".in_numbers).to eq(11063)
14
+ expect("one nineteen ten oh five".in_numbers).to eq(1191005)
15
+ end
16
+
17
+ end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: numbers_in_words
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Burns
8
+ - Dimid Duchovny
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2015-11-30 00:00:00.000000000 Z
12
+ date: 2015-12-02 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: activesupport
@@ -41,6 +42,7 @@ dependencies:
41
42
  description: "#in_words method for integers and #in_numbers for strings"
42
43
  email:
43
44
  - markthedeveloper@gmail.com
45
+ - dimidd@gmail.com
44
46
  executables:
45
47
  - spec
46
48
  extensions: []
@@ -71,8 +73,10 @@ files:
71
73
  - spec/non_monkey_patch_spec.rb
72
74
  - spec/number_group_spec.rb
73
75
  - spec/numbers_in_words_spec.rb
76
+ - spec/numerical_strings_spec.rb
74
77
  - spec/spec_helper.rb
75
78
  - spec/words_in_numbers_spec.rb
79
+ - spec/years_spec.rb
76
80
  homepage: http://github.com/markburns/numbers_in_words
77
81
  licenses: []
78
82
  metadata: {}
@@ -102,5 +106,7 @@ test_files:
102
106
  - spec/non_monkey_patch_spec.rb
103
107
  - spec/number_group_spec.rb
104
108
  - spec/numbers_in_words_spec.rb
109
+ - spec/numerical_strings_spec.rb
105
110
  - spec/spec_helper.rb
106
111
  - spec/words_in_numbers_spec.rb
112
+ - spec/years_spec.rb