class ActiveSupport::OrderedHash
The order of iteration over hashes in Ruby 1.8 is undefined. For example,
you do not know the order in which keys
will return keys, or
each
yield pairs. ActiveSupport::OrderedHash
implements a hash that preserves insertion order, as in Ruby 1.9:
oh = ActiveSupport::OrderedHash.new oh[:a] = 1 oh[:b] = 2 oh.keys # => [:a, :b], this order is guaranteed
ActiveSupport::OrderedHash
is namespaced to prevent conflicts
with other implementations.
Public Class Methods
# File lib/active_support/ordered_hash.rb, line 73 def self.[](*args) ordered_hash = new if (args.length == 1 && args.first.is_a?(Array)) args.first.each do |key_value_pair| next unless (key_value_pair.is_a?(Array)) ordered_hash[key_value_pair[0]] = key_value_pair[1] end return ordered_hash end unless (args.size % 2 == 0) raise ArgumentError.new("odd number of arguments for Hash") end args.each_with_index do |val, ind| next if (ind % 2 != 0) ordered_hash[val] = args[ind + 1] end ordered_hash end
In MRI the Hash class is core and written in C. In particular, methods are programmed with explicit C function calls and polymorphism is not honored.
For example, []= is crucial in this implementation to maintain the @keys array but hash.c invokes rb_hash_aset() originally. This prevents method reuse through inheritance and forces us to reimplement stuff.
For instance, we cannot use the inherited merge! because albeit the algorithm itself would work, our []= is not being called at all by the C code.
# File lib/active_support/ordered_hash.rb, line 68 def initialize(*args, &block) super @keys = [] end
Public Instance Methods
# File lib/active_support/ordered_hash.rb, line 103 def []=(key, value) @keys << key unless has_key?(key) super end
# File lib/active_support/ordered_hash.rb, line 174 def clear super @keys.clear self end
# File lib/active_support/ordered_hash.rb, line 108 def delete(key) if has_key? key index = @keys.index(key) @keys.delete_at index end super end
# File lib/active_support/ordered_hash.rb, line 116 def delete_if super sync_keys! self end
# File lib/active_support/ordered_hash.rb, line 160 def each return to_enum(:each) unless block_given? @keys.each {|key| yield [key, self[key]]} self end
# File lib/active_support/ordered_hash.rb, line 148 def each_key return to_enum(:each_key) unless block_given? @keys.each { |key| yield key } self end
# File lib/active_support/ordered_hash.rb, line 166 def each_pair return to_enum(:each_pair) unless block_given? @keys.each {|key| yield key, self[key]} self end
# File lib/active_support/ordered_hash.rb, line 154 def each_value return to_enum(:each_value) unless block_given? @keys.each { |key| yield self[key]} self end
# File lib/active_support/ordered_hash.rb, line 28 def encode_with(coder) coder.represent_seq '!omap', map { |k,v| { k => v } } end
Returns true to make sure that this hash is extractable via
Array#extract_options!
# File lib/active_support/ordered_hash.rb, line 51 def extractable_options? true end
# File lib/active_support/ordered_hash.rb, line 97 def initialize_copy(other) super # make a deep copy of keys @keys = other.keys end
# File lib/active_support/ordered_hash.rb, line 212 def inspect "#<OrderedHash #{super}>" end
# File lib/active_support/ordered_hash.rb, line 208 def invert OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}] end
# File lib/active_support/ordered_hash.rb, line 132 def keys @keys.dup end
# File lib/active_support/ordered_hash.rb, line 197 def merge(other_hash, &block) dup.merge!(other_hash, &block) end
# File lib/active_support/ordered_hash.rb, line 186 def merge!(other_hash) if block_given? other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v } else other_hash.each { |k, v| self[k] = v } end self end
# File lib/active_support/ordered_hash.rb, line 46 def nested_under_indifferent_access self end
# File lib/active_support/ordered_hash.rb, line 128 def reject(&block) dup.reject!(&block) end
# File lib/active_support/ordered_hash.rb, line 122 def reject! super sync_keys! self end
When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
# File lib/active_support/ordered_hash.rb, line 202 def replace(other) super @keys = other.keys self end
# File lib/active_support/ordered_hash.rb, line 180 def shift k = @keys.first v = delete(k) [k, v] end
# File lib/active_support/ordered_hash.rb, line 217 def sync_keys! @keys.delete_if {|k| !has_key?(k)} end
# File lib/active_support/ordered_hash.rb, line 144 def to_a @keys.map { |key| [ key, self[key] ] } end
# File lib/active_support/ordered_hash.rb, line 140 def to_hash self end
# File lib/active_support/ordered_hash.rb, line 32 def to_yaml(opts = {}) if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck? return super end YAML.quick_emit(self, opts) do |out| out.seq(taguri) do |seq| each do |k, v| seq.add(k => v) end end end end
# File lib/active_support/ordered_hash.rb, line 24 def to_yaml_type "!tag:yaml.org,2002:omap" end
# File lib/active_support/ordered_hash.rb, line 136 def values @keys.collect { |key| self[key] } end