Skip to content

Instantly share code, notes, and snippets.

@bravoecho
Last active August 29, 2015 14:05
Show Gist options
  • Save bravoecho/73ea750acae37ac2a29d to your computer and use it in GitHub Desktop.
Save bravoecho/73ea750acae37ac2a29d to your computer and use it in GitHub Desktop.
Week bitmask / bitarray
require 'set'
class Week
DAYS = %w[Mon Tue Wed Thu Fri Sat Sun]
REVERTED_DAYS = DAYS.reverse
attr_reader :day_bitmask
def self.new(*days)
from_days(*days)
end
def self.from_days(*days)
instance = allocate
instance.instance_eval { @day_bitmask = days_to_bitmask(*days) }
instance
end
def self.from_bitmask(int)
instance = allocate
instance.instance_eval { @day_bitmask = int }
instance
end
def to_bitmask
@day_bitmask
end
# An integer acts like a bitarray.
# This first transforms the bitmask to a bitarray, and then takes the
# days whose corresponsing bit is set to 1.
def to_a
DAYS.select.with_index { |_, i| day_bitmask[i] == 1 }
end
def days
to_a
end
private
def days_to_bitmask(*days)
bitarray = days_to_bitarray(*days)
bitarray_to_bitmask(bitarray)
end
def days_to_bitarray(*days)
# This is probably super yagni
days = Set.new(days)
REVERTED_DAYS.map { |day| days.include?(day) ? 1 : 0 }
end
def bitarray_to_bitmask(ba)
ba.join.to_i(2)
end
end
Week.from_days('Mon', 'Thu', 'Fri', 'Sat').to_bitmask # => 57
Week.from_days('Mon', 'Thu', 'Sun').to_bitmask # => 73
Week.from_days('Mon', 'Thu', 'Sat').to_bitmask # => 41
Week.from_days('Fri', 'Sat', 'Sun').to_bitmask # => 112
Week.from_days('Mon', 'Thu').to_bitmask # => 9
Week.from_days('Thu').to_bitmask # => 8
week = Week.from_bitmask(112) # => #<Week:0x007f7c7db5e428 @day_bitmask=112>
week.to_a # => ["Fri", "Sat", "Sun"]
# Has all? => & operator
# Yes => value & target == value
41 & 57 # => 41
9 & 57 # => 9
8 & 57 # => 8
# No => value & target != value
41 & 73 # => 9
9 & 112 # => 0
# Has any?
# Yes => value & target > 0
9 & 57 # => 9
112 & 57 # => 48
# No => value & target == 0
9 & 112 # => 0
8 & 112 # => 0
#
# Old version
# def to_a
# day_bitmask
# .to_s(2)
# .rjust(7, '0')
# .reverse
# .each_char
# .with_index
# .with_object([]) { |(bit, i), result|
# result << DAYS[i] if bit == '1'
# }
# end
# Old version
# DAYS_WITH_INDEX = Hash[DAYS.map.with_index { |day, idx| [day, idx] }]
# def days_to_bitmask(*days)
# # compact because sadly values_at(missing_key) returns [nil]
# DAYS_WITH_INDEX.values_at(*days).compact.reduce(0) do |result, day_idx|
# result + 2**day_idx
# end
# end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment