top_n 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +12 -0
- data/Gemfile +21 -0
- data/Guardfile +28 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +30 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/lib/top_n.rb +178 -0
- data/test/helper.rb +19 -0
- data/test/test_adding_bottom.rb +88 -0
- data/test/test_adding_top.rb +88 -0
- data/test/test_creation.rb +47 -0
- data/test/test_data.rb +24 -0
- data/top_n.gemspec +74 -0
- metadata +171 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a5fc5435e211281c5d59fa26d155260a0359a216
|
4
|
+
data.tar.gz: f3cd1bf16b9c9c11ad87793a7c0741e1fe7f314a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 471670b61a3b91ff867fab94bcd414a9f766fb7367579122ea57d5f4d58e7b964e4551d77bb4ce35a4b2523df7282dce8a845791c0c8c7677ff4ec46c82b4071
|
7
|
+
data.tar.gz: 24330b140e01c80493d4434720672bef428825bbb8a6133ab234443c77bf498583f4c91360e2a33524da9d4e02a20814ec450a33ed14498a3dbe1c6b435eb08b
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "minitest", ">= 0"
|
10
|
+
gem "yard", "~> 0.7"
|
11
|
+
gem "rdoc", "~> 3.12"
|
12
|
+
gem "bundler", "~> 1.0"
|
13
|
+
gem "jeweler", "~> 1.8.7"
|
14
|
+
gem "guard"
|
15
|
+
gem 'guard-minitest'
|
16
|
+
|
17
|
+
platforms :rbx do
|
18
|
+
# gem 'racc'
|
19
|
+
gem 'rubysl'
|
20
|
+
end
|
21
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard :minitest do
|
5
|
+
# with Minitest::Unit
|
6
|
+
watch(%r{^test/(.*)\/?test_(.*)\.rb})
|
7
|
+
watch(%r{^lib/(.*/)?([^/]+)\.rb}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
|
8
|
+
watch(%r{^test/test_helper\.rb}) { 'test' }
|
9
|
+
|
10
|
+
# with Minitest::Spec
|
11
|
+
# watch(%r{^spec/(.*)_spec\.rb})
|
12
|
+
# watch(%r{^lib/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
|
13
|
+
# watch(%r{^spec/spec_helper\.rb}) { 'spec' }
|
14
|
+
|
15
|
+
# Rails 4
|
16
|
+
# watch(%r{^app/(.+)\.rb}) { |m| "test/#{m[1]}_test.rb" }
|
17
|
+
# watch(%r{^app/controllers/application_controller\.rb}) { 'test/controllers' }
|
18
|
+
# watch(%r{^app/controllers/(.+)_controller\.rb}) { |m| "test/integration/#{m[1]}_test.rb" }
|
19
|
+
# watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" }
|
20
|
+
# watch(%r{^lib/(.+)\.rb}) { |m| "test/lib/#{m[1]}_test.rb" }
|
21
|
+
# watch(%r{^test/.+_test\.rb})
|
22
|
+
# watch(%r{^test/test_helper\.rb}) { 'test' }
|
23
|
+
|
24
|
+
# Rails < 4
|
25
|
+
# watch(%r{^app/controllers/(.*)\.rb}) { |m| "test/functional/#{m[1]}_test.rb" }
|
26
|
+
# watch(%r{^app/helpers/(.*)\.rb}) { |m| "test/helpers/#{m[1]}_test.rb" }
|
27
|
+
# watch(%r{^app/models/(.*)\.rb}) { |m| "test/unit/#{m[1]}_test.rb" }
|
28
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Michael Graff
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
= top_n
|
2
|
+
|
3
|
+
{<img src="https://travis-ci.org/skandragon/top_n.png?branch=master" alt="Build Status" />}[https://travis-ci.org/skandragon/top_n]
|
4
|
+
|
5
|
+
This gem allows tracking of the top N (or bottom N) keys added to a list.
|
6
|
+
Each key can have an optional value, which is then added to a list of values for that key.
|
7
|
+
|
8
|
+
Key types must respond to the usual comparison operators: <, <=, >, >=, and <=>. Values may be any object type, and need not be unique. Values are not examined in any way, simply added to an internal list.
|
9
|
+
|
10
|
+
If a <key, value> pair is added twice, it will store the value twice for that key, and return it twice in the list for that key, when that key is retrieved.
|
11
|
+
|
12
|
+
Once the top N keys are added, adding smaller keys will be ignored. Adding a larger key will drop the then-smallest value. Adding another value at an existing key will simply result in
|
13
|
+
|
14
|
+
For example, track the top 1,000 EverQuest 2 characters with the highest strenth, the most quests completed, or items crafted, out of the roughly 2.4 million characters which currently exist.
|
15
|
+
|
16
|
+
== Contributing to this project
|
17
|
+
|
18
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
19
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
20
|
+
* Fork the project.
|
21
|
+
* Start a feature/bugfix branch.
|
22
|
+
* Commit and push until you are happy with your contribution.
|
23
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
24
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
25
|
+
|
26
|
+
== Copyright
|
27
|
+
|
28
|
+
Copyright (c) 2013 Michael Graff. See LICENSE.txt for
|
29
|
+
further details.
|
30
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "top_n"
|
18
|
+
gem.homepage = "http://github.com/skandragon/top_n"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Track the top (or bottom) N keys added to a list}
|
21
|
+
gem.description = %Q{Track the top (or bottom) N keys added to a list, and discard the remainder.}
|
22
|
+
gem.email = "explorer@flame.org"
|
23
|
+
gem.authors = ["Michael Graff"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :test
|
36
|
+
|
37
|
+
require 'yard'
|
38
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/lib/top_n.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
##
|
2
|
+
# This class tracks the top (or bottom) N values for a set of keys.
|
3
|
+
#
|
4
|
+
# As keys and values are added, only the largest (or smallest) keys will
|
5
|
+
# be recorded. As larger (or smaller) keys are added, unneeded items are
|
6
|
+
# removed.
|
7
|
+
#
|
8
|
+
# Keys may be any object that is comparable with < and >. Values may be
|
9
|
+
# any object, and are not processed beyond appending them to an internal
|
10
|
+
# list.
|
11
|
+
#
|
12
|
+
# @note
|
13
|
+
# Note that while the number of keys is restricted, the values are not.
|
14
|
+
# This can cause memory issues if many values are added to the same key.
|
15
|
+
# If this is the type of data you are tracking, you may need a different
|
16
|
+
# solution.
|
17
|
+
class TopN
|
18
|
+
# The maxinum number of keys which will be tracked.
|
19
|
+
# @return [Fixnum] the configured maximum number of keys to be tracked.
|
20
|
+
attr_reader :maxsize
|
21
|
+
|
22
|
+
# The configured direction.
|
23
|
+
# @return [Symbol] either :top or :bottom.
|
24
|
+
attr_reader :direction
|
25
|
+
|
26
|
+
# The current number of keys we are tracking.
|
27
|
+
# @return [FixNum] the count, which will be 0 up to :maxsize.
|
28
|
+
attr_reader :size
|
29
|
+
|
30
|
+
# The current value of the minimum (:top) or maximum (:bottom) key.
|
31
|
+
# @return [Object] the threshold key.
|
32
|
+
attr_reader :threshold_key
|
33
|
+
|
34
|
+
# The currently tracked data as one blob. This is tied to the
|
35
|
+
# current implementation, so its use is not recommended.
|
36
|
+
# @return [Hash] the internal data structure containing the keys and
|
37
|
+
# values. The keys to the returned Hash are the tracked keys, and
|
38
|
+
# the values at those keys is the list of values.
|
39
|
+
attr_reader :data
|
40
|
+
|
41
|
+
##
|
42
|
+
# Create a new TopN object. Options available:
|
43
|
+
#
|
44
|
+
# @param [Hash] options the options used to configure the TopN object.
|
45
|
+
#
|
46
|
+
# @option options [Fixnum] :maxsize The maximum number of keys to track.
|
47
|
+
# Must be a positive Fixnum. Defaults to 100.
|
48
|
+
#
|
49
|
+
# @option options [Symbol] :direction Configure the direction.
|
50
|
+
# If this is :top, the largest keys will be maintained. If :bottom,
|
51
|
+
# the smallest keys will be maintained. Any other value throws an
|
52
|
+
# exception.
|
53
|
+
# Defaults to :top.
|
54
|
+
#
|
55
|
+
# @raise [ArgumentError] if an invalid value is detected for any option.
|
56
|
+
#
|
57
|
+
# @example Create with default options
|
58
|
+
# topn = TopN.new
|
59
|
+
#
|
60
|
+
# @example Create with a maximum size of 10, and track smaller values
|
61
|
+
# topn = TopN.new(maxsize: 10, direction: :bottom)
|
62
|
+
#
|
63
|
+
def initialize(options = {})
|
64
|
+
options = {
|
65
|
+
maxsize: 100,
|
66
|
+
direction: :top,
|
67
|
+
}.merge(options)
|
68
|
+
|
69
|
+
@maxsize = options[:maxsize]
|
70
|
+
@direction = options[:direction]
|
71
|
+
@data = {}
|
72
|
+
@size = 0
|
73
|
+
@threshold_key = nil
|
74
|
+
|
75
|
+
unless [:top, :bottom].include?(@direction)
|
76
|
+
raise ArgumentError.new("direction must be :top or :bottom")
|
77
|
+
end
|
78
|
+
|
79
|
+
unless @maxsize.is_a?Fixnum
|
80
|
+
raise ArgumentError.new("maxsize must be a Fixnum")
|
81
|
+
end
|
82
|
+
|
83
|
+
if @maxsize <= 0
|
84
|
+
raise ArgumentError.new("maxsize must be >= 1")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Add a key, value pair.
|
90
|
+
#
|
91
|
+
# @param [Object] key the key, which must be compariable with < and >.
|
92
|
+
# @param [Object] value the value, which is added to the key's list of
|
93
|
+
# values. Adding the same value to a key multiple times results in
|
94
|
+
# duplicate values being recorded.
|
95
|
+
#
|
96
|
+
# If the key already exists, the value will be appended to the existing list
|
97
|
+
# of values at that key.
|
98
|
+
#
|
99
|
+
# If an existing (key, value) is permitted, and will result in the list of
|
100
|
+
# values at that key having the same value multiple times.
|
101
|
+
#
|
102
|
+
# @return [Array] if the value was added to the key's list.
|
103
|
+
#
|
104
|
+
# @return [nil] if the value was not added because the key is too small or
|
105
|
+
# large to be tracked.
|
106
|
+
def add(key, value)
|
107
|
+
if @direction == :top
|
108
|
+
add_top(key, value)
|
109
|
+
else
|
110
|
+
add_bottom(key, value)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Find and return the list of values for a key.
|
116
|
+
#
|
117
|
+
# @return [Array<Object>] the list of values for 'key'.
|
118
|
+
#
|
119
|
+
# @return [nil] if the key does not exist.
|
120
|
+
def find(key)
|
121
|
+
@data[key]
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Return the list of currently tracked keys.
|
126
|
+
#
|
127
|
+
# @return [Array<Object>] the list of values for this key.
|
128
|
+
# Order is not guaranteed to match the oder which they were added.
|
129
|
+
def keys
|
130
|
+
@data.keys
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
##
|
136
|
+
# Add a (key, value) when the direction is :top.
|
137
|
+
def add_top(key, value)
|
138
|
+
@threshold_key ||= key
|
139
|
+
|
140
|
+
if @size >= @maxsize
|
141
|
+
return nil if key < @threshold_key
|
142
|
+
@data.delete(@threshold_key)
|
143
|
+
end
|
144
|
+
|
145
|
+
unless @data.has_key?key
|
146
|
+
@data[key] = []
|
147
|
+
@size += 1 if @size < @maxsize
|
148
|
+
end
|
149
|
+
|
150
|
+
@data[key] << value
|
151
|
+
|
152
|
+
@threshold_key = key if key < @threshold_key
|
153
|
+
|
154
|
+
@data[key]
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# Add a (key, value) when the direction is :bottom.
|
159
|
+
def add_bottom(key, value)
|
160
|
+
@threshold_key ||= key
|
161
|
+
|
162
|
+
if @size >= @maxsize
|
163
|
+
return nil if key > @threshold_key
|
164
|
+
@data.delete(@threshold_key)
|
165
|
+
end
|
166
|
+
|
167
|
+
unless @data.has_key?key
|
168
|
+
@data[key] = []
|
169
|
+
@size += 1 if @size < @maxsize
|
170
|
+
end
|
171
|
+
|
172
|
+
@data[key] << value
|
173
|
+
|
174
|
+
@threshold_key = key if key > @threshold_key
|
175
|
+
|
176
|
+
@data[key]
|
177
|
+
end
|
178
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'minitest/autorun'
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
require 'top_n'
|
15
|
+
|
16
|
+
class MiniTest::Unit::TestCase
|
17
|
+
end
|
18
|
+
|
19
|
+
MiniTest.autorun
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestAddingBottom < Minitest::Test
|
4
|
+
def test_threshold_key_is_nil_on_create
|
5
|
+
topn = TopN.new(direction: :bottom)
|
6
|
+
assert_nil topn.threshold_key
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_adding_updates_threshold_key
|
10
|
+
topn = TopN.new(direction: :bottom)
|
11
|
+
assert topn.add(5, 5), "add(5, 5) failed"
|
12
|
+
assert_equal 5, topn.threshold_key
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_adding_increments_size
|
16
|
+
topn = TopN.new(direction: :bottom)
|
17
|
+
assert topn.add(1, 1), "add(1, 1) failed"
|
18
|
+
assert topn.size == 1, "size == 1 failed"
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_adding_without_exceeding_adds
|
22
|
+
topn = TopN.new(direction: :bottom, maxsize: 10)
|
23
|
+
assert topn.add(5, 5), "add(5, 5) failed"
|
24
|
+
assert topn.find(5) == [5], "find(5) failed"
|
25
|
+
assert_equal 5, topn.threshold_key
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_adding_sets_threshold_key
|
29
|
+
topn = TopN.new(direction: :bottom, maxsize: 10)
|
30
|
+
assert topn.add(5, 5), "add(5, 5) failed"
|
31
|
+
assert topn.find(5) == [5], "find(5) failed"
|
32
|
+
assert_equal 5, topn.threshold_key
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_adding_without_exceeding
|
36
|
+
topn = TopN.new(direction: :bottom, maxsize: 10)
|
37
|
+
assert topn.add(5, 5), "add(5, 5) failed"
|
38
|
+
assert topn.add(10, 10), "add(10, 10) failed"
|
39
|
+
assert topn.find(5) == [5], "find(5) failed"
|
40
|
+
assert topn.find(10) == [10], "find(5) failed"
|
41
|
+
assert_equal 10, topn.threshold_key
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_adding_larger_without_exceeding_sets_threshold_key
|
45
|
+
topn = TopN.new(direction: :bottom, maxsize: 10)
|
46
|
+
assert topn.add(5, 5), "add(5, 5) failed"
|
47
|
+
assert_equal 5, topn.threshold_key
|
48
|
+
assert topn.add(10, 10), "add(10, 10) failed"
|
49
|
+
assert_equal 10, topn.threshold_key
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_adding_smaller_without_exceeding_sets_threshold_key
|
53
|
+
topn = TopN.new(direction: :bottom, maxsize: 10)
|
54
|
+
assert topn.add(10, 10), "add(10, 10) failed"
|
55
|
+
assert_equal 10, topn.threshold_key
|
56
|
+
assert topn.add(5, 5), "add(5, 5) failed"
|
57
|
+
assert_equal 10, topn.threshold_key
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_adding_two_values_to_the_same_key
|
61
|
+
topn = TopN.new(direction: :bottom, maxsize: 10)
|
62
|
+
assert topn.add(1, 1), "add(1, 1) failed"
|
63
|
+
assert topn.add(1, 2), "add(1, 2) failed"
|
64
|
+
assert_equal [1, 2], topn.find(1).sort
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_adding_smaller_key_when_limit_will_exceed
|
68
|
+
topn = TopN.new(direction: :bottom, maxsize: 2)
|
69
|
+
assert topn.add(3, 3), "add(3, 3) failed"
|
70
|
+
assert topn.add(2, 2), "add(2, 2) failed"
|
71
|
+
assert topn.add(1, 1), "add(1, 1) failed"
|
72
|
+
assert_equal 2, topn.size
|
73
|
+
assert_equal [1], topn.find(1)
|
74
|
+
assert_equal [2], topn.find(2)
|
75
|
+
assert_nil topn.find(3)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_adding_larger_key_when_limit_will_exceed
|
79
|
+
topn = TopN.new(direction: :bottom, maxsize: 2)
|
80
|
+
assert topn.add(1, 1), "add(1, 1) failed"
|
81
|
+
assert topn.add(2, 2), "add(2, 2) failed"
|
82
|
+
assert_nil topn.add(3, 3)
|
83
|
+
assert_equal 2, topn.size
|
84
|
+
assert_equal [1], topn.find(1)
|
85
|
+
assert_equal [2], topn.find(2)
|
86
|
+
assert_nil topn.find(3)
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestAddingTop < Minitest::Test
|
4
|
+
def test_threshold_key_is_nil_on_create
|
5
|
+
topn = TopN.new
|
6
|
+
assert_nil topn.threshold_key
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_adding_updates_threshold_key
|
10
|
+
topn = TopN.new
|
11
|
+
assert topn.add(5, 5), "add(5, 5) failed"
|
12
|
+
assert_equal 5, topn.threshold_key
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_adding_increments_size
|
16
|
+
topn = TopN.new
|
17
|
+
assert topn.add(1, 1), "add(1, 1) failed"
|
18
|
+
assert topn.size == 1, "size == 1 failed"
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_adding_without_exceeding_adds
|
22
|
+
topn = TopN.new(maxsize: 10)
|
23
|
+
assert topn.add(5, 5), "add(5, 5) failed"
|
24
|
+
assert topn.find(5) == [5], "find(5) failed"
|
25
|
+
assert_equal 5, topn.threshold_key
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_adding_sets_threshold_key
|
29
|
+
topn = TopN.new(maxsize: 10)
|
30
|
+
assert topn.add(5, 5), "add(5, 5) failed"
|
31
|
+
assert topn.find(5) == [5], "find(5) failed"
|
32
|
+
assert_equal 5, topn.threshold_key
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_adding_without_exceeding
|
36
|
+
topn = TopN.new(maxsize: 10)
|
37
|
+
assert topn.add(5, 5), "add(5, 5) failed"
|
38
|
+
assert topn.add(10, 10), "add(10, 10) failed"
|
39
|
+
assert topn.find(5) == [5], "find(5) failed"
|
40
|
+
assert topn.find(10) == [10], "find(5) failed"
|
41
|
+
assert_equal 5, topn.threshold_key
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_adding_larger_without_exceeding_sets_threshold_key
|
45
|
+
topn = TopN.new(maxsize: 10)
|
46
|
+
assert topn.add(5, 5), "add(5, 5) failed"
|
47
|
+
assert_equal 5, topn.threshold_key
|
48
|
+
assert topn.add(10, 10), "add(10, 10) failed"
|
49
|
+
assert_equal 5, topn.threshold_key
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_adding_smaller_without_exceeding_sets_threshold_key
|
53
|
+
topn = TopN.new(maxsize: 10)
|
54
|
+
assert topn.add(10, 10), "add(10, 10) failed"
|
55
|
+
assert_equal 10, topn.threshold_key
|
56
|
+
assert topn.add(5, 5), "add(5, 5) failed"
|
57
|
+
assert_equal 5, topn.threshold_key
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_adding_two_values_to_the_same_key
|
61
|
+
topn = TopN.new(maxsize: 10)
|
62
|
+
assert topn.add(1, 1), "add(1, 1) failed"
|
63
|
+
assert topn.add(1, 2), "add(1, 2) failed"
|
64
|
+
assert_equal [1, 2], topn.find(1).sort
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_adding_larger_key_when_limit_will_exceed
|
68
|
+
topn = TopN.new(maxsize: 2)
|
69
|
+
assert topn.add(1, 1), "add(1, 1) failed"
|
70
|
+
assert topn.add(2, 2), "add(1, 1) failed"
|
71
|
+
assert topn.add(3, 3), "add(1, 1) failed"
|
72
|
+
assert_equal 2, topn.size
|
73
|
+
assert_nil topn.find(1)
|
74
|
+
assert_equal [2], topn.find(2)
|
75
|
+
assert_equal [3], topn.find(3)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_adding_smaller_key_when_limit_will_exceed
|
79
|
+
topn = TopN.new(maxsize: 2)
|
80
|
+
assert topn.add(3, 3), "add(1, 1) failed"
|
81
|
+
assert topn.add(2, 2), "add(1, 1) failed"
|
82
|
+
assert_nil topn.add(1, 1)
|
83
|
+
assert_equal 2, topn.size
|
84
|
+
assert_nil topn.find(1)
|
85
|
+
assert_equal [2], topn.find(2)
|
86
|
+
assert_equal [3], topn.find(3)
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestTopN < Minitest::Test
|
4
|
+
def test_creation_without_arguments
|
5
|
+
topn = TopN.new
|
6
|
+
assert topn
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_creation_without_arguments_sets_maxsize_to_some_positive_default
|
10
|
+
topn = TopN.new
|
11
|
+
assert topn.maxsize > 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_creation_with_maxsize
|
15
|
+
topn = TopN.new(maxsize: 100)
|
16
|
+
assert topn.maxsize == 100
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_creation_with_direction
|
20
|
+
topn = TopN.new(direction: :bottom)
|
21
|
+
assert topn.direction == :bottom
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_creation_raises_assertion_with_bad_direction
|
25
|
+
assert_raises(ArgumentError) {
|
26
|
+
TopN.new(direction: :flarg)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_creation_raises_assertion_with_zero_maxsize
|
31
|
+
assert_raises(ArgumentError) {
|
32
|
+
TopN.new(maxsize: 0)
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_creation_raises_assertion_with_negative_maxsize
|
37
|
+
assert_raises(ArgumentError) {
|
38
|
+
TopN.new(maxsize: -1)
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_creation_raises_assertion_with_non_fixnum_maxsize
|
43
|
+
assert_raises(ArgumentError) {
|
44
|
+
TopN.new(maxsize: 'foo')
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
data/test/test_data.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestData < Minitest::Test
|
4
|
+
def test_data_is_empty_on_create
|
5
|
+
topn = TopN.new(maxsize: 2)
|
6
|
+
assert_equal({}, topn.data)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_data_has_keys
|
10
|
+
topn = TopN.new(maxsize: 2)
|
11
|
+
assert topn.add(1, 1), "add(1, 1) failed"
|
12
|
+
assert topn.add(2, 2), "add(1, 1) failed"
|
13
|
+
assert topn.add(3, 3), "add(1, 1) failed"
|
14
|
+
assert_equal [2, 3], topn.data.keys.sort
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_keys
|
18
|
+
topn = TopN.new(maxsize: 2)
|
19
|
+
assert topn.add(1, 1), "add(1, 1) failed"
|
20
|
+
assert topn.add(2, 2), "add(1, 1) failed"
|
21
|
+
assert topn.add(3, 3), "add(1, 1) failed"
|
22
|
+
assert_equal [2, 3], topn.keys.sort
|
23
|
+
end
|
24
|
+
end
|
data/top_n.gemspec
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "top_n"
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Michael Graff"]
|
12
|
+
s.date = "2013-12-15"
|
13
|
+
s.description = "Track the top (or bottom) N keys added to a list, and discard the remainder."
|
14
|
+
s.email = "explorer@flame.org"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".travis.yml",
|
21
|
+
"Gemfile",
|
22
|
+
"Guardfile",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"lib/top_n.rb",
|
28
|
+
"test/helper.rb",
|
29
|
+
"test/test_adding_bottom.rb",
|
30
|
+
"test/test_adding_top.rb",
|
31
|
+
"test/test_creation.rb",
|
32
|
+
"test/test_data.rb",
|
33
|
+
"top_n.gemspec"
|
34
|
+
]
|
35
|
+
s.homepage = "http://github.com/skandragon/top_n"
|
36
|
+
s.licenses = ["MIT"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubygems_version = "2.0.14"
|
39
|
+
s.summary = "Track the top (or bottom) N keys added to a list"
|
40
|
+
|
41
|
+
if s.respond_to? :specification_version then
|
42
|
+
s.specification_version = 4
|
43
|
+
|
44
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
45
|
+
s.add_development_dependency(%q<minitest>, [">= 0"])
|
46
|
+
s.add_development_dependency(%q<yard>, ["~> 0.7"])
|
47
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
48
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0"])
|
49
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.7"])
|
50
|
+
s.add_development_dependency(%q<guard>, [">= 0"])
|
51
|
+
s.add_development_dependency(%q<guard-minitest>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<rubysl>, [">= 0"])
|
53
|
+
else
|
54
|
+
s.add_dependency(%q<minitest>, [">= 0"])
|
55
|
+
s.add_dependency(%q<yard>, ["~> 0.7"])
|
56
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
57
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
58
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.7"])
|
59
|
+
s.add_dependency(%q<guard>, [">= 0"])
|
60
|
+
s.add_dependency(%q<guard-minitest>, [">= 0"])
|
61
|
+
s.add_dependency(%q<rubysl>, [">= 0"])
|
62
|
+
end
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<minitest>, [">= 0"])
|
65
|
+
s.add_dependency(%q<yard>, ["~> 0.7"])
|
66
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
67
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
68
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.7"])
|
69
|
+
s.add_dependency(%q<guard>, [">= 0"])
|
70
|
+
s.add_dependency(%q<guard-minitest>, [">= 0"])
|
71
|
+
s.add_dependency(%q<rubysl>, [">= 0"])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
metadata
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: top_n
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Graff
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: yard
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rdoc
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.12'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.12'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: jeweler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.8.7
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.8.7
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: guard
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: guard-minitest
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubysl
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Track the top (or bottom) N keys added to a list, and discard the remainder.
|
126
|
+
email: explorer@flame.org
|
127
|
+
executables: []
|
128
|
+
extensions: []
|
129
|
+
extra_rdoc_files:
|
130
|
+
- LICENSE.txt
|
131
|
+
- README.rdoc
|
132
|
+
files:
|
133
|
+
- .travis.yml
|
134
|
+
- Gemfile
|
135
|
+
- Guardfile
|
136
|
+
- LICENSE.txt
|
137
|
+
- README.rdoc
|
138
|
+
- Rakefile
|
139
|
+
- VERSION
|
140
|
+
- lib/top_n.rb
|
141
|
+
- test/helper.rb
|
142
|
+
- test/test_adding_bottom.rb
|
143
|
+
- test/test_adding_top.rb
|
144
|
+
- test/test_creation.rb
|
145
|
+
- test/test_data.rb
|
146
|
+
- top_n.gemspec
|
147
|
+
homepage: http://github.com/skandragon/top_n
|
148
|
+
licenses:
|
149
|
+
- MIT
|
150
|
+
metadata: {}
|
151
|
+
post_install_message:
|
152
|
+
rdoc_options: []
|
153
|
+
require_paths:
|
154
|
+
- lib
|
155
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - '>='
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: '0'
|
165
|
+
requirements: []
|
166
|
+
rubyforge_project:
|
167
|
+
rubygems_version: 2.0.14
|
168
|
+
signing_key:
|
169
|
+
specification_version: 4
|
170
|
+
summary: Track the top (or bottom) N keys added to a list
|
171
|
+
test_files: []
|