range_array 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +54 -0
- data/Rakefile +1 -0
- data/lib/range_array/range.rb +43 -0
- data/lib/range_array/range_array.rb +23 -0
- data/lib/range_array/version.rb +3 -0
- data/lib/range_array.rb +3 -0
- data/range_array.gemspec +24 -0
- data/spec/range_array_spec.rb +107 -0
- data/spec/range_merge_spec.rb +48 -0
- metadata +108 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Steve Flinter
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# RangeArray
|
2
|
+
|
3
|
+
This is a very simple subclass of the `Array` class, aimed specifically at supporting `Ranges`, rather than general objects.
|
4
|
+
|
5
|
+
When a new range is added to the array (using the `<<` method), the RangeArray ensures that there are no overlapping ranges, and if necessary, merges any overlapping ranges into a single range.
|
6
|
+
|
7
|
+
For example, if the RangeArray holds the ranges (1..10) and (20..30), appending the range (5..25) will merge all ranges into a single range (1..30).
|
8
|
+
|
9
|
+
At this stage, only the `<<` has been overridden, so no other methods are checked to guard against non-range members.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'range_array'
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install range_array
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
In your `Gemfile`:
|
28
|
+
|
29
|
+
```gem 'range_array'```
|
30
|
+
|
31
|
+
Simple example:
|
32
|
+
|
33
|
+
require 'range_array'
|
34
|
+
ra = RangeArray.new
|
35
|
+
ra << (1..10) # [(1..10)]
|
36
|
+
ra << (20..30) # [(1..10), (20..30)]
|
37
|
+
ra << (5..25) # [(1..30)]
|
38
|
+
|
39
|
+
Also works for dates:
|
40
|
+
|
41
|
+
require 'range_array'
|
42
|
+
require 'date'
|
43
|
+
ra = RangeArray.new
|
44
|
+
ra << (Date.parse('2013-01-01') .. Date.parse('2013-05-01'))
|
45
|
+
ra << (Date.parse('2013-10-01') .. Date.parse('2013-12-31'))
|
46
|
+
ra << (Date.parse('2013-04-01') .. Date.parse('2013-11-01')) # [('2013-01-01' .. '2013-12-31')]
|
47
|
+
|
48
|
+
## Contributing
|
49
|
+
|
50
|
+
1. Fork it
|
51
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
52
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
53
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
54
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Range
|
2
|
+
def merge(r)
|
3
|
+
return self if self == r
|
4
|
+
|
5
|
+
return self if fully_overlaps?(r)
|
6
|
+
return r if fully_overlapped_by?(r)
|
7
|
+
|
8
|
+
return (r.min .. self.max) if adjoins?(r) || start_overlapped_by?(r)
|
9
|
+
return (self.min .. r.max) if adjoined_by?(r) || end_overlapped_by?(r)
|
10
|
+
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def merge!(r)
|
15
|
+
merge(r) || self
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def fully_overlaps?(r)
|
21
|
+
start_overlapped_by?(r) && end_overlapped_by?(r)
|
22
|
+
end
|
23
|
+
|
24
|
+
def fully_overlapped_by?(r)
|
25
|
+
r.cover?(self.min) && r.cover?(self.max)
|
26
|
+
end
|
27
|
+
|
28
|
+
def start_overlapped_by?(r)
|
29
|
+
self.cover?(r.max)
|
30
|
+
end
|
31
|
+
|
32
|
+
def end_overlapped_by?(r)
|
33
|
+
self.cover?(r.min)
|
34
|
+
end
|
35
|
+
|
36
|
+
def adjoins?(r)
|
37
|
+
r.max.succ == self.min
|
38
|
+
end
|
39
|
+
|
40
|
+
def adjoined_by?(r)
|
41
|
+
self.max.succ == r.min
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class RangeArray < Array
|
2
|
+
alias append <<
|
3
|
+
|
4
|
+
def <<(range)
|
5
|
+
return unless range.class == Range
|
6
|
+
super eliminate_overlaps(merged_range(range))
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def merged_range(range)
|
12
|
+
self.inject(range) do |memo_range, r|
|
13
|
+
memo_range.merge!(r)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def eliminate_overlaps(range)
|
18
|
+
self.delete_if do |r|
|
19
|
+
r.merge(range)
|
20
|
+
end
|
21
|
+
range
|
22
|
+
end
|
23
|
+
end
|
data/lib/range_array.rb
ADDED
data/range_array.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'range_array/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "range_array"
|
8
|
+
spec.version = RangeArray::VERSION
|
9
|
+
spec.authors = ["Steve Flinter"]
|
10
|
+
spec.email = ["stephen@flinter.com"]
|
11
|
+
spec.description = %q{Gem to create and manage an array of ranges}
|
12
|
+
spec.summary = %q{Gem to create and manage an array of ranges}
|
13
|
+
spec.homepage = "https://github.com/sflinter/range_array"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/range_array'
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
describe RangeArray do
|
6
|
+
it "can create a new RangeArray" do
|
7
|
+
ra = RangeArray.new
|
8
|
+
expect(ra.class).to eq(RangeArray)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "can add new items" do
|
12
|
+
ra = RangeArray.new
|
13
|
+
ra << (1..2)
|
14
|
+
expect(ra.size).to eq(1)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "can add two items" do
|
18
|
+
ra = RangeArray.new
|
19
|
+
ra << (1..10)
|
20
|
+
ra << (20..30)
|
21
|
+
expect(ra.size).to eq(2)
|
22
|
+
expect(ra.first).to eq(1..10)
|
23
|
+
expect(ra.last).to eq(20..30)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "won't add non-range items" do
|
27
|
+
ra = RangeArray.new
|
28
|
+
ra << (1..2)
|
29
|
+
ra << 4
|
30
|
+
expect(ra.size).to eq(1)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "can append non-consecutive intervals" do
|
34
|
+
ra = RangeArray.new
|
35
|
+
ra << (1..5)
|
36
|
+
ra << (10..20)
|
37
|
+
expect(ra.size).to eq(2)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "can append consecutive intervals" do
|
41
|
+
ra = RangeArray.new
|
42
|
+
ra << (1..2)
|
43
|
+
ra << (3..4)
|
44
|
+
expect(ra.size).to eq(1)
|
45
|
+
expect(ra.first).to eq(1..4)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "can append consecutive intervals in reverse order" do
|
49
|
+
ra = RangeArray.new
|
50
|
+
ra << (3..4)
|
51
|
+
ra << (1..2)
|
52
|
+
expect(ra.size).to eq(1)
|
53
|
+
expect(ra.first).to eq(1..4)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "can add simple overlapping intervals" do
|
57
|
+
ra = RangeArray.new
|
58
|
+
ra << (1..5)
|
59
|
+
ra << (4..10)
|
60
|
+
expect(ra.size).to eq(1)
|
61
|
+
expect(ra.first).to eq(1..10)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "can add simple overlapping intervals in reverse order" do
|
65
|
+
ra = RangeArray.new
|
66
|
+
ra << (4..10)
|
67
|
+
ra << (1..5)
|
68
|
+
expect(ra.size).to eq(1)
|
69
|
+
expect(ra.first).to eq(1..10)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "can add identical ranges without duplication" do
|
73
|
+
ra = RangeArray.new
|
74
|
+
ra << (1..10)
|
75
|
+
ra << (1..10)
|
76
|
+
expect(ra.size).to eq(1)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "can join multiple intervals" do
|
80
|
+
ra = RangeArray.new
|
81
|
+
ra << (1..4)
|
82
|
+
ra << (8..10)
|
83
|
+
ra << (5..7)
|
84
|
+
expect(ra.size).to eq(1)
|
85
|
+
expect(ra.first).to eq(1..10)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "can join overlapping intervals" do
|
89
|
+
ra = RangeArray.new
|
90
|
+
ra << (1..4)
|
91
|
+
ra << (6..10)
|
92
|
+
ra << (3..8)
|
93
|
+
expect(ra.size).to eq(1)
|
94
|
+
expect(ra.first).to eq(1..10)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "can join overlapping date intervals" do
|
98
|
+
ra = RangeArray.new
|
99
|
+
ra << (Date.parse('2013-01-01') .. Date.parse('2013-05-01'))
|
100
|
+
ra << (Date.parse('2013-10-01') .. Date.parse('2013-12-31'))
|
101
|
+
expect(ra.size).to eq(2)
|
102
|
+
|
103
|
+
ra << (Date.parse('2013-04-01') .. Date.parse('2013-11-01'))
|
104
|
+
expect(ra.size).to eq(1)
|
105
|
+
expect(ra.first).to eq(Date.parse('2013-01-01') .. Date.parse('2013-12-31'))
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/range_array'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
describe Range do
|
5
|
+
it "doesn't merge where there's no overlap" do
|
6
|
+
expect((1..10).merge(20..30)).to be(nil)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns self where there's no overlap" do
|
10
|
+
expect((1..10).merge!(20..30)).to eq(1..10)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "merges identical ranges" do
|
14
|
+
expect((1..10).merge(1..10)).to eq(1..10)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "merges overlapping ranges" do
|
18
|
+
expect((1..10).merge(5..20)).to eq(1..20)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "merges out of order overlapping ranges" do
|
22
|
+
expect((5..20).merge(1..10)).to eq(1..20)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "merges consecutive ranges" do
|
26
|
+
expect((1..10).merge(11..20)).to eq(1..20)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "merges out of order consecutive ranges" do
|
30
|
+
expect((11..20).merge(1..10)).to eq(1..20)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "merges fully overlapping ranges" do
|
34
|
+
expect((1..20).merge(5..15)).to eq(1..20)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "merges fully overlapping out of order ranges" do
|
38
|
+
expect((5..15).merge(1..20)).to eq(1..20)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "merges date ranges" do
|
42
|
+
r1 = (Date.parse('2013-01-01') .. Date.parse('2013-07-01'))
|
43
|
+
r2 = (Date.parse('2013-06-01') .. Date.parse('2013-12-31'))
|
44
|
+
r3 = (Date.parse('2013-01-01') .. Date.parse('2013-12-31'))
|
45
|
+
|
46
|
+
expect(r1.merge(r2)).to eq(r3)
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: range_array
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Steve Flinter
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Gem to create and manage an array of ranges
|
63
|
+
email:
|
64
|
+
- stephen@flinter.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE.txt
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- lib/range_array.rb
|
75
|
+
- lib/range_array/range.rb
|
76
|
+
- lib/range_array/range_array.rb
|
77
|
+
- lib/range_array/version.rb
|
78
|
+
- range_array.gemspec
|
79
|
+
- spec/range_array_spec.rb
|
80
|
+
- spec/range_merge_spec.rb
|
81
|
+
homepage: https://github.com/sflinter/range_array
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 1.8.25
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: Gem to create and manage an array of ranges
|
106
|
+
test_files:
|
107
|
+
- spec/range_array_spec.rb
|
108
|
+
- spec/range_merge_spec.rb
|