2007-12-30

ruby way之hashes

1创建一个hash

就想创建一个数组一样,我们能够使用Hash的[]方法来创建一个hash:

puts a1 = Hash.[]("flat",3,"curved",2)
puts a2 = Hash.[]("flat"=>3,"curved"=>2)
puts b1 = Hash["flat",3,"curved",2]
puts b2 = Hash["flat"=>3,"curved"=>2]
puts c2 = {"flat"=>3,"curved"=>2}


相似的,我们还可以使用new来创建一个hash:

puts d = Hash.new         # 创建一个空的hash
puts e = Hash.new(99)     #  创建一个默认值为99的hash
puts f = Hash.new("a"=>3) # 
puts e["angled"]          # 99
puts e.inspect            # {}
puts f["b"]               # {"a"=>3} (刚好默认值也是一个hash)
puts f.inspect            # {}


2指定一个默认值给hash

一个hash,如果你使用一个不存在的key去取值的话,将会返回给你一个nil,这里我们还可以自己定义默认值,从而来代替nil,或者我们还可以使用default=方法来改变它的默认值:

puts a = Hash.new("missing")  # default value object is "missing"
puts a["hello"]                       # "missing"
puts a.default="nothing"
puts a["hello"]                       # "nothing"
puts a["good"] << "bye"               # "nothingbye"
puts a.default                # "nothingbye"


如果当一个key不存在时,你想抛出一个异常来代替返回默认值的话,你可以使用fetch方法,当你使用fetch 方法的时候如果key不存在,它则会抛出一个IndexError异常,它还有第二个参数,这个参数就是当你key不存在的时候所返回的值:

a = {"flat"=>3,"curved"=>2,"angled"=>5}
a.fetch("pointed")                   # IndexError
puts a.fetch("curved","na")               # 2
puts a.fetch("x","na")                    # "na"
puts a.fetch("flat") {|x| x.upcase}       # 3
puts a.fetch("pointed") {|x| x.upcase}    # "POINTED"


3存取或者加一个键值对

hash也有像数组一样的[]和[]=方法,还有一个store,它是[]=方法的别名:

a = {}
a["flat"] = 3        # {"flat"=>3}
a.[]=("curved",2)    # {"flat"=>3,"curved"=>2}
a.store("angled",5)  # {"flat"=>3,"curved"=>2,"angled"=>5}

puts a


在这里要注意,nil对象也可以作为一个健或者值:

b={}
b[2]      # nil
b[3]=nil
b         # {3=>nil}
b[2].nil? # true
b[3].nil? # true
b[nil]=5
b         # {3=>nil,nil=>5}
b[nil]    # 5
b[b[3]]   # 5


4删除一个键值对

删除的话我们能够使用 clear, delete, delete_if, reject, reject!, 和shift 这些方法都能做到。

clear 将会把一个hash清空.
shift则将会删除掉最前面的一个键值对:

a = {1=>2, 3=>4}
b = a.shift       # [1,2] a现在是[3=>4]


delete删除一个指定位置的键值对,如果不存在这个位置,则将会返回block里的字符串:

a = {1=>1, 2=>4, 3=>9, 4=>16}
a.delete(3)                     # 9
# a is now {1=>1, 2=>4, 4=>16}
a.delete(5)                     # nil in this case
a.delete(6) { "not found" }     # "not found"


5迭代一个hash

我们能够使用each方法来做这个,它还有each_key, each_pair和 each_value方法,其中each_pair是each方法的别名:

{"a"=>3,"b"=>2}.each do |key, val|
  print val, " from ", key, "; "    # 3 from a; 2 from b;
end


each_key 和each_value的block只有一个参数:

{"a"=>3,"b"=>2}.each_key do |key|
  print "key = #{key};"      # Prints: key = a; key = b;
end

{"a"=>3,"b"=>2}.each_value do |value|
  print "val = #{value};"    # Prints: val = 3; val = 2;
end


6转置一个hash

也就是将一个hash的键和值交换:

a = {"fred"=>"555-1122","jane"=>"555-7779"}
b = a.invert
b["555-7779"]     # "jane"


注意这里有一个问题,那就是值有可能是重复的,这时如果你使用invert的话,它将会删除掉一个重复的键值对:

a = {"fred"=>"555-7779","jane"=>"555-7779"}
b = a.invert
puts b["555-7779"]    # "jane"


7检测一个健或者值是否属于一个hash

我们能够使用has_key?或者它的任意一个别名,include?, key?, 和member?来测试一个key是否属于一个hash:

a = {"a"=>1,"b"=>2}
a.has_key? "c"        # false
a.include? "a"        # true
a.key? 2              # false
a.member? "b"         # true


你还能使用empty? 方法来测试一个hash是否为空,length来得到一个hash的大小:

a.empty?        # false
a.length        # 2


如果想测试值的话,就使用has_value?或者value?:

a.has_value? 2        # true
a.value? 99           # false


8 将一个hash转换为一个数组

可以使用to_a来讲一个hash转换成一个数组:

h = {"a"=>1,"b"=>2}
h.to_a        # ["a",1,"b",2]


如果只想把值或者健转换成数组,可以使用keys或者values :

h.keys         # ["a","b"]
h.values       # [1,2]


最终你还可以基于一个健序列,来将他们所对应的值转换换成数组:

h = {1=>"one",2=>"two",3=>"three",4=>"four","cinco"=>"five"}
h.values_at(3,"cinco",4)     # ["three","five","four"]
h.values_at(1,3)             # ["one","three"]


9 根据条件选择键值对

Hash类混入了Enumerable 模块,因此你能够使用detect (find), select, (find_all), grep, min, max, 和reject 这些方法.

detect (别名find)得到一个单独的键值对:

names = {"fred"=>"jones","jane"=>"tucker",
            "joe"=>"tucker","mary"=>"SMITH"}
 # 得到一个tucker
 names.detect {|k,v| v=="tucker" }    # ["joe","tucker"]
 # 得到一个大写的
names.find {|k,v| v==v.upcase }    # ["mary", "SMITH"]



select(别名finad_all)将会返回所有符合条件的键值对:

names.select {|k,v| v=="tucker" }
# [["joe", "tucker"], ["jane", "tucker"]]
names.find_all {|k,v| k.count("r")>0}
# [["mary", "SMITH"], ["fred", "jones"]]


10 排序一个hash

ruby排序的时候是将hash转换为一个数组,然后才进行排序的:

names = {"Jack"=>"Ruby","Monty"=>"Python",
         "Blaise"=>"Pascal", "Minnie"=>"Perl"}
list = names.sort
# list is now:
# [["Blaise","Pascal"], ["Jack","Ruby"],
#  ["Minnie","Perl"], ["Monty","Python"]]


11 合并两个散列

ruby能够使用merge(别名update)方法,来合并两个hash,如果他们中的健有相同的话,一个将会被删除:

dict = {"base"=>"foundation", "pedestal"=>"base"}
added = {"base"=>"non-acid", "salt"=>"NaCl"}
new_dict = dict.merge(added)
# {"base"=>"non-acid", "pedestal"=>"base", "salt"=>"NaCl"}


merge还可以使用block来处理,两个健有冲突的hash的合并,我们下面的代码是假设我们有两个冲突的健的话,我们选择值较小的那个键值对:

dict = {"base"=>"foundation", "pedestal"=>"base"}
added = {"base"=>"non-acid", "salt"=>"NaCl"}
new_dict = dict.merge(added) {|key,old,new| old < new ? old : new }
# {"salt"=>"NaCl", "pedestal"=>"base", "base"=>"foundation"}



12 由一个数组来创建hash

array = [2, 3, 4, 5, 6, 7]
hash = Hash[*array]
# hash is now: {2=>3, 4=>5, 6=>7}


13计算hash键的差集和交集

由于hash的key能够被转换成一个数组,因此我们能够使用一些数组的方法:

a = {"a"=>1,"b"=>2,"z"=>3}
b = {"x"=>99,"y"=>88,"z"=>77}
intersection = a.keys & b.keys
difference = a.keys - b.keys
c = a.dup.update(b)
inter = {}
intersection.each {|k| inter[k]=c[k] }
# inter is {"z"=>77}
diff={}
difference.each {|k| diff[k]=c[k] }
# diff is {"a"=>1, "b"=>2}


14使用hash作为一个Sparse Matrix

我们能够这样做:

cube = Hash.new(0)
cube[[2000,2000,2000]] = 2
z = cube[[36,24,36]]       # 0


15一个简单的可以包含重复健的hash

这个很简单的实现,看下代码就行了:

class HashDup

  def initialize(*all)
    raise IndexError if all.size % 2 != 0
    @store = {}
    if all[0]  # not nil
      keyval = all.dup
      while !keyval.empty?
        key = keyval.shift
        if @store.has_key?(key)
          @store[key] += [keyval.shift]
        else
          @store[key] = [keyval.shift]
        end
      end
    end
  end

  def store(k,v)
    if @store.has_key?(k)
      @store[k] += [v]
    else
      @store[k] = [v]
    end
  end

  def [](key)
    @store[key]
  end

  def []=(key,value)
    self.store(key,value)
  end

  def to_s
    @store.to_s
  end

  def to_a
    @store.to_a
  end

  def inspect
    @store.inspect
  end

  def keys
    result=[]
    @store.each do |k,v|
      result += ([k]*v.size)
    end
    result
  end

  def values
    @store.values.flatten
  end

  def each
    @store.each {|k,v| v.each {|y| yield k,y}}
  end

  alias each_pair each

  def each_key
    self.keys.each {|k| yield k}
  end

  def each_value
    self.values.each {|v| yield v}
  end

  def has_key? k
    self.keys.include? k
  end

  def has_value? v
    self.values.include? v
  end

  def length
    self.values.size
  end

  alias size length

  def delete k
    val = @store[k]
    @store.delete k
    val
  end

  def delete k,v
    @store[k] -= [v] if @store[k]
    v
  end

  # Other methods omitted here...

end



# This won't work... dup key will ignore
# first occurrence.
h = {1=>1, 2=>4, 3=>9, 4=>16, 2=>0}

# This will work...
h = HashDup.new(1,1, 2,4, 3,9, 4,16, 2,0)

k = h.keys         # [4, 1, 2, 2, 3]
v = h.values       # [16, 1, 4, 0, 9]

n = h.size         # 5

h.each {|k,v| puts "#{k} => #{v}"}
# Prints:
# 4 => 16
# 1 => 1
# 2 => 4
# 2 => 0
# 3 => 9
评论
发表评论

您还没有登录,请登录后发表评论

simohayha
搜索本博客
存档
最新评论