lib/top_n.rb in top_n-1.0.1 vs lib/top_n.rb in top_n-1.1.0
- old
+ new
@@ -12,40 +12,33 @@
# @note
# Note that while the number of keys is restricted, the values are not.
# This can cause memory issues if many values are added to the same key.
# If this is the type of data you are tracking, you may need a different
# solution.
-class TopN
+class TopN < Hash
# The maxinum number of keys which will be tracked.
# @return [Fixnum] the configured maximum number of keys to be tracked.
- attr_reader :maxsize
+ attr_reader :maxkeys
# The configured direction.
# @return [Symbol] either :top or :bottom.
attr_reader :direction
- # The current number of keys we are tracking.
- # @return [FixNum] the count, which will be 0 up to :maxsize.
- attr_reader :size
-
# The current value of the minimum (:top) or maximum (:bottom) key.
# @return [Object] the threshold key.
attr_reader :threshold_key
- # The currently tracked data as one blob. This is tied to the
- # current implementation, so its use is not recommended.
- # @return [Hash] the internal data structure containing the keys and
- # values. The keys to the returned Hash are the tracked keys, and
- # the values at those keys is the list of values.
- attr_reader :data
+ alias_method :super_store, :store
+ alias_method :super_bracket_assign, :[]=
+ alias_method :super_bracket, :[]
##
# Create a new TopN object. Options available:
#
# @param [Hash] options the options used to configure the TopN object.
#
- # @option options [Fixnum] :maxsize The maximum number of keys to track.
+ # @option options [Fixnum] :maxkeys The maximum number of keys to track.
# Must be a positive Fixnum. Defaults to 100.
#
# @option options [Symbol] :direction Configure the direction.
# If this is :top, the largest keys will be maintained. If :bottom,
# the smallest keys will be maintained. Any other value throws an
@@ -56,41 +49,41 @@
#
# @example Create with default options
# topn = TopN.new
#
# @example Create with a maximum size of 10, and track smaller values
- # topn = TopN.new(maxsize: 10, direction: :bottom)
+ # topn = TopN.new(maxkeys: 10, direction: :bottom)
#
- def initialize(options = {})
+ def initialize(default = nil, options = {})
options = {
- maxsize: 100,
+ maxkeys: 100,
direction: :top,
}.merge(options)
options.keys.each do |opt|
- unless [:maxsize, :direction].include?opt
+ unless [:maxkeys, :direction].include?opt
raise ArgumentError.new("invalid option #{opt}")
end
end
- @maxsize = options[:maxsize]
+ @maxkeys = options[:maxkeys]
@direction = options[:direction]
- @data = {}
- @size = 0
@threshold_key = nil
unless [:top, :bottom].include?(@direction)
raise ArgumentError.new("direction must be :top or :bottom")
end
- unless @maxsize.is_a?Fixnum
- raise ArgumentError.new("maxsize must be a Fixnum")
+ unless @maxkeys.is_a?Fixnum
+ raise ArgumentError.new("maxkeys must be a Fixnum")
end
- if @maxsize <= 0
- raise ArgumentError.new("maxsize must be >= 1")
+ if @maxkeys <= 0
+ raise ArgumentError.new("maxkeys must be >= 1")
end
+
+ super(default)
end
##
# Add a key, value pair.
#
@@ -103,82 +96,62 @@
# of values at that key.
#
# If an existing (key, value) is permitted, and will result in the list of
# values at that key having the same value multiple times.
#
- # @return [Array] if the value was added to the key's list.
- #
- # @return [nil] if the value was not added because the key is too small or
- # large to be tracked.
- def add(key, value)
+ # @return [Object] the value passed in. This will be returned even if
+ # the value is not added because there are too many keys already present.
+ def store(key, value)
if @direction == :top
add_top(key, value)
else
add_bottom(key, value)
end
end
- ##
- # Find and return the list of values for a key.
- #
- # @return [Array<Object>] the list of values for 'key'.
- #
- # @return [nil] if the key does not exist.
- def find(key)
- @data[key]
+ # Behave like #store, with the same semantics.
+ def []=(key, value)
+ store(key, value)
end
- ##
- # Return the list of currently tracked keys.
- #
- # @return [Array<Object>] the list of values for this key.
- # Order is not guaranteed to match the oder which they were added.
- def keys
- @data.keys
- end
-
private
##
# Add a (key, value) when the direction is :top.
def add_top(key, value)
@threshold_key ||= key
- if @data.has_key?key
- @data[key] << value
+ if has_key?key
+ fetch(key) << value
else
- if @size >= @maxsize
- return nil if key < @threshold_key
- @data.delete(@threshold_key)
- @size -= 1
- @threshold_key = @data.keys.min
+ if size >= @maxkeys
+ return value if key < @threshold_key
+ delete(@threshold_key)
+ @threshold_key = keys.min
end
- @data[key] = [ value ]
- @size += 1
+ super_store(key, [ value ])
@threshold_key = key if key < @threshold_key
end
- @data[key]
+ value
end
##
# Add a (key, value) when the direction is :bottom.
def add_bottom(key, value)
@threshold_key ||= key
- if @data.has_key?key
- @data[key] << value
+ if has_key?key
+ fetch(key) << value
else
- if @size >= @maxsize
- return nil if key > @threshold_key
- @data.delete(@threshold_key)
- @size -= 1
- @threshold_key = @data.keys.max
+ if size >= @maxkeys
+ return value if key > @threshold_key
+ delete(@threshold_key)
+ @threshold_key = keys.max
end
- @data[key] = [ value ]
- @size += 1
+ super_store(key, [ value ])
@threshold_key = key if key > @threshold_key
end
- @data[key]
+ value
end
end