Skip to content

Commit

Permalink
Improve filter, using a block to get specific time for a given value.
Browse files Browse the repository at this point in the history
  • Loading branch information
ioquatix committed Jun 20, 2016
1 parent 9f46dff commit b4fd476
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 29 deletions.
55 changes: 32 additions & 23 deletions lib/periodical/filter.rb
Expand Up @@ -26,35 +26,44 @@ module Periodical
module Filter
# Keep count sorted objects per period.
class Period
KeepOldest = Proc.new do |t1, t2|
t1 > t2
end

KeepYoungest = Proc.new do |t1, t2|
t1 < t2
end

# Given times a and b, should we prefer a?
ORDER = {
# We want `a` if `a` < `b`, i.e. it's older.
old: ->(a, b){a < b},

# We want `a` if `a` > `b`, i.e. it's newer.
new: ->(a, b){a > b}
}

# @param count the number of items we should retain.
def initialize(count)
@count = count
end

def filter(values, options = {})
# @param order can be a key in ORDER or a lambda.
# @param block is applied to the value and should typically return a Time instance.
def filter(values, keep: :old, &block)
slots = {}

keep = (options[:keep] == :youngest) ? KeepYoungest : KeepOldest

keep = ORDER.fetch(keep, keep)
values.each do |value|
k = key(value)

# We want to keep the newest backup if possible (<).
next if slots.key?(k) and keep.call(value, slots[k])

slots[k] = value
time = block_given? ? yield(value) : value

granular_key = key(time)

# We filter out this value if the slot is already full and we prefer the existing value.
if existing_value = slots[granular_key]
existing_time = block_given? ? yield(existing_value) : existing_value
next if keep.call(existing_time, time)
end

slots[granular_key] = value
end

sorted_values = slots.values.sort

return sorted_values[0...@count]
return sorted_values.first(@count)
end

def key(t)
Expand Down Expand Up @@ -113,11 +122,11 @@ def <<(period)
@periods[period.class] = period
end

def filter(values, options = {})
def filter(values, **options, &block)
filtered_values = Set.new

@periods.values.each do |period|
filtered_values += period.filter(values, options)
filtered_values += period.filter(values, **options, &block)
end

return filtered_values, (Set.new(values) - filtered_values)
Expand Down
2 changes: 1 addition & 1 deletion lib/periodical/version.rb
Expand Up @@ -19,5 +19,5 @@
# THE SOFTWARE.

module Periodical
VERSION = "1.0.1"
VERSION = "1.1.0"
end
8 changes: 3 additions & 5 deletions spec/periodical/filter_spec.rb
Expand Up @@ -36,11 +36,9 @@ module Periodical::PeriodSpec
policy << Periodical::Filter::Daily.new(3)

selected, rejected = policy.filter(dates)
expect(selected.count).to be 3
expect(rejected.count).to be 3

# Keep oldest is the default policy
expect(selected).to be_include(dates[0])
expect(selected).to include(*dates.first(3))
expect(rejected).to include(*dates.last(3))
end

it "should keep youngest" do
Expand All @@ -52,7 +50,7 @@ module Periodical::PeriodSpec
policy = Periodical::Filter::Policy.new
policy << Periodical::Filter::Monthly.new(1)

selected, rejected = policy.filter(dates, :keep => :youngest)
selected, rejected = policy.filter(dates, :keep => :new)
expect(selected.count).to be 1
expect(rejected.count).to be 1

Expand Down

0 comments on commit b4fd476

Please sign in to comment.