class CICPHash

  1. cicphash.rb
Superclass: Object

Case Insensitive Case Preserving Hash

CICPHash has the exact same interface as Hash, but is case insensitive and case preserving. Any value can be used as a key. However, you cannot have two keys in the same CICPHash that would be the same if when converted to strings would be equal or differing only in case.

For example, all of the following keys would be considered equalivalent: 'ab', 'Ab', 'AB', 'aB', :ab, :Ab, :AB, :aB

CICPHash uses a last match wins policy. If an key-value pair is added to a CICPHash and a case insensitive variant of the key is already in the hash the instance of the key in the hash becomes the same the most recently added key (and obviously the value is updated to reflect the new value).

You can change the rules determining which keys are equal by modifying the private convert_key method. By default, it is set to key.to_s.downcase. This method should produce the same output for any keys that you want to consider equal. For example, if you want :a and :A to be equal but :a to be different than “a”, maybe key.inspect.downcase would work for you.

Included modules

  1. Enumerable

Public Instance Aliases

each_pair -> each
include? -> has_key?
index -> key
indexes -> values_at
indices -> values_at
key? -> has_key?
member? -> has_key?
merge! -> update
size -> length
store -> []=
value? -> has_value?

Public Class methods

[] (*items)
[show source]
# File cicphash.rb, line 49
def self.[](*items)
  if items.length % 2 != 0
    if items.length == 1 && items.first.is_a?(Hash)
      new.merge(items.first)
    else
      raise ArgumentError, "odd number of arguments for CICPHash"
    end
  else
    hash = new
    loop do
      break if items.length == 0
      key = items.shift
      value = items.shift
      hash[key] = value
    end
    hash
  end
end
new (*default, &block)
[show source]
# File cicphash.rb, line 68
def initialize(*default, &block)
  if default.length > 1 || default.length == 1 && block_given?
    raise ArgumentError, "wrong number of arguments"
  end
  @name_hash = {}
  @hash = {}
  @default = default.first unless block_given?
  @default_proc = Proc.new(&block) if block_given?
end

Public Instance methods

== (hash)
[show source]
# File cicphash.rb, line 78
def ==(hash)
  to_hash == hash.to_hash
end
[] (key)
[show source]
# File cicphash.rb, line 82
def [](key)
  new_key = convert_key(key)
  if @hash.include?(new_key)
    @hash[new_key]
  elsif @default_proc
    @default_proc.call(self, key)
  else
    @default
  end
end
[]= (key, value)
[show source]
# File cicphash.rb, line 93
def []=(key, value)
  new_key = convert_key(key)
  @name_hash[new_key] = key
  @hash[new_key] = value
end
assoc (obj)
[show source]
# File cicphash.rb, line 311
def assoc(obj)
  obj = convert_key(obj)
  each do |k, v|
    if convert_key(k) == obj
      return [k, v]
    end
  end
  nil
end
clear ()
[show source]
# File cicphash.rb, line 100
def clear
  @name_hash.clear
  @hash.clear
end
clone ()
[show source]
# File cicphash.rb, line 105
def clone
  s = super
  s.instance_variable_set(:@hash, @hash.clone)
  s.instance_variable_set(:@name_hash, @name_hash.clone)
  s
end
default ()
[show source]
# File cicphash.rb, line 112
def default
  @default
end
default= (value)
[show source]
# File cicphash.rb, line 116
def default=(value)
  @default = value
end
default_proc ()
[show source]
# File cicphash.rb, line 120
def default_proc
  @default_proc
end
delete (key)
[show source]
# File cicphash.rb, line 124
def delete(key)
  new_key = convert_key(key)
  @name_hash.delete(new_key)
  @hash.delete(new_key)
end
delete_if (&block)
[show source]
# File cicphash.rb, line 130
def delete_if(&block)
  hash = CICPHash.new
  each{|key, value| block.call(key, value) ? delete(key) : (hash[key] = value)}
  hash
end
dup ()
[show source]
# File cicphash.rb, line 136
def dup
  s = super
  s.instance_variable_set(:@hash, @hash.dup)
  s.instance_variable_set(:@name_hash, @name_hash.dup)
  s
end
each ()
[show source]
# File cicphash.rb, line 143
def each
  @hash.each{|key, value| yield @name_hash[key], value }
end
each_key ()
[show source]
# File cicphash.rb, line 148
def each_key
  @hash.each_key{|key| yield @name_hash[key] }
end
each_value ()
[show source]
# File cicphash.rb, line 152
def each_value
  @hash.each_value{|value| yield value }
end
empty? ()
[show source]
# File cicphash.rb, line 156
def empty?
  @hash.empty?
end
fetch (key, *default, &block)
[show source]
# File cicphash.rb, line 160
def fetch(key, *default, &block)
  raise ArgumentError, "wrong number of arguments (#{default.length+1} for 2)" if default.length > 1
  if include?(key)
    self[key]
  elsif block_given?
    block.call(key)
  elsif default.length == 1
    default.first
  else
    raise IndexError, "key not found"
  end
end
flatten (*a)
[show source]
# File cicphash.rb, line 323
def flatten(*a)
  if a.empty?
    to_a.flatten(1)
  else
    to_a.flatten(*a)
  end
end
has_key? (key)
[show source]
# File cicphash.rb, line 173
def has_key?(key)
  @hash.has_key?(convert_key(key))
end
has_value? (value)
[show source]
# File cicphash.rb, line 180
def has_value?(value)
  @hash.has_value?(value)
end
inspect ()
[show source]
# File cicphash.rb, line 196
def inspect
  to_hash.inspect
end
invert ()
[show source]
# File cicphash.rb, line 200
def invert
  hash = CICPHash.new
  each{|key, value| hash[value] = key}
  hash
end
keep_if (&block)
[show source]
# File cicphash.rb, line 331
def keep_if(&block)
  to_a.each do |k, v|
    delete(k) unless yield(k, v)
  end
  self
end
key (value)
[show source]
# File cicphash.rb, line 186
def key(value)
  @name_hash[@hash.key(value)]
end
keys ()
[show source]
# File cicphash.rb, line 206
def keys
  @name_hash.values
end
length ()
[show source]
# File cicphash.rb, line 210
def length
  @hash.length
end
merge (hash, &block)
[show source]
# File cicphash.rb, line 215
def merge(hash, &block)
  new_hash = CICPHash.new.merge!(self)
  hash.each do |key, value| 
    new_hash[key] = if block_given? && new_hash.include?(key)
      block.call(key, new_hash[key], hash[key])
    else 
      value
    end
  end
  new_hash
end
rassoc (obj)
[show source]
# File cicphash.rb, line 338
def rassoc(obj)
  each do |k, v|
    if v == obj
      return [k, v]
    end
  end
  nil
end
rehash ()
[show source]
# File cicphash.rb, line 227
def rehash
  @name_hash.keys.each do |key|
    new_key = @name_hash[key].to_s.downcase 
    if new_key != key
      @name_hash[new_key] = @name_hash.delete(key)
      @hash[new_key] = @hash.delete(key)
    end
  end
  self
end
reject (&block)
[show source]
# File cicphash.rb, line 238
def reject(&block)
  hash = CICPHash.new
  each{|key, value| hash[key] = self[key] unless block.call(key, value)}
  hash
end
reject! (&block)
[show source]
# File cicphash.rb, line 244
def reject!(&block)
  hash = CICPHash.new
  changes = false
  each{|key, value| block.call(key, value) ? (changes = true; delete(key)) : (hash[key] = value)}
  changes ? hash : nil
end
replace (hash)
[show source]
# File cicphash.rb, line 251
def replace(hash)
  clear
  update(hash)
end
select (&block)
[show source]
# File cicphash.rb, line 256
def select(&block)
  array = []
  each{|key, value| array << [key, value] if block.call(key, value)}
  array
end
select! (&block)
[show source]
# File cicphash.rb, line 347
def select!(&block)
  mod = false
  to_a.each do |k, v|
    unless yield(k, v)
      mod = true
      delete(k)
    end
  end
  self if mod
end
shift ()
[show source]
# File cicphash.rb, line 262
def shift
  return nil if @name_hash.length == 0
  key, value = @name_hash.shift
  [value, @hash.delete(key)]
end
sort (&block)
[show source]
# File cicphash.rb, line 268
def sort(&block)
  block_given? ? to_a.sort(&block) : to_a.sort
end
to_a ()
[show source]
# File cicphash.rb, line 272
def to_a
  array = []
  each{|key, value| array << [key, value]}
  array
end
to_hash ()
[show source]
# File cicphash.rb, line 278
def to_hash
  hash = {}
  each{|key, value| hash[key] = value}
  hash
end
to_s ()
[show source]
# File cicphash.rb, line 284
def to_s
  to_a.join
end
update (hash, &block)
[show source]
# File cicphash.rb, line 288
def update(hash, &block)
  hash.each do |key, value| 
    self[key] = if block_given? && include?(key)
      block.call(key, self[key], hash[key])
    else 
      value
    end
  end
  self
end
values ()
[show source]
# File cicphash.rb, line 300
def values
  @hash.values
end
values_at (*keys)
[show source]
# File cicphash.rb, line 304
def values_at(*keys)
  keys.collect{|key| self[key]}
end