您的位置:首页 > 编程语言 > Ruby

Ruby for Rails 最佳实践十一

2013-10-08 14:35 651 查看


第十一章 集合、容器和可枚举性


一、数组和散列的比较

数组是有序的对象集合,有序意味着可以基于连续的数字索引从集合中选择对象。

散列是无序的集合,这意味着不能说第一/第二个或第N个元素是什么。散列将对象以成对的方法存储起来,每一对有一个键和一个值,通过键来索引值。散列在其它语言中有时也叫字典或联合数组。



二、使用数组

1. 创建一个新数组

a = Array.new

a = []

a = [1,2,"three",4, [] ]



用 Array.new 的好处是,允许指定数组的大小,并可对其内容进行初始化

>> Array.new(3)

=> [nil, nil, nil]

>> Array.new(3,"abc")

=> ["abc", "abc", "abc"]



甚至可以为 Array.new 提供代码块

>> n = 0

=> 0

>> Array.new(3) { n += 1; n * 10 }

=> [10, 20, 30]



2. 插入、检索和删除数组元素

(1)一次读写多个数组元素

>> a = ["red","orange","yellow","purple","gray","indigo","violet"]

=> ["red", "orange", "yellow", "purple", "gray", "indigo", "violet"]

>> a[3,2]

=> ["purple", "gray"]

>> a[3,2] = "green", "blue"

=> ["green", "blue"]

>> a

=> ["red", "orange", "yellow", "green", "blue", "indigo", "violet"]



(2)对数组头或尾进行操作的特殊方法

使用 unshift 给数组头部添加一个对象

a = [1,2,3,4]

a.unshift(0) # 结果:[0,1,2,3,4]



使用 push (或 << 方法)给数组屋部添加一个对象

a = [1,2,3,4]

a << 5 # 结果:[1,2,3,4,5]

a.push(6,7,8) # 结果:[1,2,3,4,5,6,7,8]



使用 shift 和 pop 从数组的头和尾删除一个对象

a = [1,2,3,4,5]

print "The original array: "

p a

popped = a.pop

print "The popped item: "

puts popped

print "The new state of the array: "

p a

shifted = a.shift

print "The shifted item: "

puts shifted

print "The new state of the array: "

p a

输出结果如下

The original array: [1, 2, 3, 4, 5]

The popped item: 5

The new state of the array: [1, 2, 3, 4]

The shifted item: 1

The new state of the array: [2, 3, 4]



3. 合并数组

(1)使用 concat 方法,合并多个数组,并且永久性地改变了接收者的内容

[1,2,3].concat([4,5,6])



(2)另一种方法是使用 + 方法进行合并,不改变接收者的内容

[1,2,3] + [4,5,6]



(3)使用 replace 替换数组

>> a = [1,2,3]

=> [1, 2, 3]

>> a.replace([4,5,6])

=> [4, 5, 6]

>> a

=> [4, 5, 6]

此方法与重新赋值是不一样的,在于变量的引用不同



(4)使用 zip 并行遍历两个数组,产生一个新的数组

>> [1,2,3].zip([4,5,6])

=> [[1, 4], [2, 5], [3, 6]]



4. 数组转换

(1)使用 flatten 进行数组的平坦化

>> [1,2,[3,4,[5,6],7],[[[8,9]]]].flatten

=> [1, 2, 3, 4, 5, 6, 7, 8, 9]



(2)使用 reverse 方法反转数组内容(也有一个感叹号版本)

>>[1,2,3,4].reverse

=> [4, 3, 2, 1]



(3)使用 join 方法把数组转换成字符串

>> ["abc", "def", 123].join(", ")

=> "abc, def, 123"



(4)使用 uniq 对数组进行唯一化处理,删除原来数组中重复的元素

>> [1,2,3,1,4,3,5,1].uniq

=> [1, 2, 3, 4, 5]



5. 数组迭代、过滤及查询

(1)数组迭代

[1,2,3,4,5].each {|x| puts x * 10 }



使用 each 方法的变体 each_with_index,在遍历时每次通过匿名调用给代码块传递两个值:当前数组元素及其数组索引(从0开始)

["a","b","c"].each_with_index {|x,i| puts "element #{i} is #{x}" }

输出如下

element 0 is a

element 1 is b

element 2 is c



(2)数组过滤

使用 find 方法,返回第一个满足代码块条件的元素

>> [1,2,3,4,5,6,7,8,9,10].find {|n| n > 5 }

=> 6



使用 find_all (或 select)方法,返回所有满足代码块条件的元素

>> a = [1,2,3,4,5,6,7,8,9,10]

=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

>> a.find_all {|item| item > 5 }

=> [6, 7, 8, 9, 10]

>> a.find_all {|item| item > 100 }

=> []



使用 reject 方法找出不满足代码条件的元素

>> a.reject {|item| item > 5 }

=> [1, 2, 3, 4, 5]



(3)常用的数组查询方法
方法名/调用示例

含义

a.size(同义词:length)

a.empty?

a.include?(item)

a.any? { |item| test }

a.all? { |item| test }

数组中元素的个数

a是空数组返回真;否则为假

数组a包含item时为真;否则为假

有一个元素通过测试就为真;否则为假

所有元素都通过测试就为真;否则为假


三、散列

1. 创建一个新的散列

h = {} 或 Hash.new

创建带有数据的散列使用 [] 方法

Hash["Connecticut" => "CT",

"Delaware" => "DE" ]



2. 插入、检索和删除散列键值对

(1)给散列添加键—值对,键值是唯一的

state_hash["New York"] = "NY"

该语句是下面这条语句的糖衣版本

state_hash.[]=("New York", "NY")

也可以使用相同功能的 store 方法来完成

state_hash.store("New York", "NY")



(2)从散列检索值

conn_abbrev = state_hash["Connecticut"]

另一种方法是使用 fetch 方法

conn_abbrev = state_hash.fetch("Connecticut")

注:fetch 与 [] 方法不同之处:查找一个不存在的键时,fetch 抛出异常而 [] 返回 nil。



还可以在一个操作中使用多个键来检索多个值,需要使用 values_at 方法

two_states = state_hash.values_at("New Jersey","Delaware")

结果为 ["NJ","DE"]



3. 指定和获取默认值

>> h = Hash.new(0)

=> {}

>> h["no such key!"]

=> 0

这种方法可以在第一次用到时,自动添加键



如果不想让不存在的键时自动添加到散列,并设置默认值的话,可以这样

h = Hash.new {|hash,key| hash[key] = 0 }

可以这样来触发

>> h["new key!"]

=> 0

>> h

=> {"new key!"=>0}



4. 合并散列

(1)破坏型操作通过方法 update 进行,如果第二个散列与第一个散列有相同的键,那么第一个散列中的键—值对会被永久性地覆盖

h1 = {"Smith" => "John",

"Jones" => "Jane" }

h2 = {"Smith" => "Jim" }

h1.update(h2)

puts h1["Smith"] # 输出: Jim



(2)要进行非破坏型散列合并,需要使用 merge 方法,merge! 与 update 是同义词

h1 = {"Smith" => "John",

"Jones" => "Jane" }

h2 = {"Smith" => "Jim" }

h3 = h1.merge(h2)

p h1["Smith"] # 输出: John



5. 散列转换

(1)反转散列Hash#invert:交换键和值

>> h = { 1 => "one", 2 => "two" }

=> {1=>"one", 2=>"two"}

>> h.invert

=> {"two"=>2, "one"=>1}



(2)清空散列Hash#clear

>> {1 => "one", 2 => "two" }.clear

=> {}



(3)替代散列的内容 replace

>> { 1 => "one", 2 => "two" }.replace({ 10 => "ten", 20 => "twenty"})

=> {10 => "ten", 20 => "twenty"}



6. 散列的迭代、过滤和查询

(1)一个完整的键—值对通过匿名调用以一个两元素数组的形式传给代码块

{1 => "one", 2 => "two" }.each do |key,value|

puts "The word for #{key} is #{value}."

end

输出结果如下

The word for 1 is one.

The word for 2 is two.



(2)迭代遍历所有的键或值

>> h = {1 => "one", 2 => "two" }

=> {1=>"one", 2=>"two"}

>> h.keys

=> [1, 2]

>> h.values

=> ["one", "two"]

也可以直接对键或值进行迭代

h = {"apple" => "red", "banana" => "yellow", "orange" => "orange" }

h.each_key {|k| puts "The next key is #{k}." }

h.each_value {|v| puts "The next value is #{v}." }



(3)散列的过滤操作

>> { 1 => "one", 2 => "two", 3 => "three" }.select {|k,v| k > 1 }

=> [[2, "two"], [3, "three"]]



或使用 find 方法,返回一个元素或 nil

>> {1 => "un", 2 => "deux", 3 => "trois" }.find {|k,v| k == 3 }

=> [3, "trois"]



还可以对散列进行映射操作

>> { 1 => "one", 2 => "two", 3 => "three" }.map {|k,v| v.upcase }

=> ["ONE", "TWO", "THREE"]



7. 常用的散列查询方法及其含义
方法名/调用示例

含义

h.has_key?(1)

h.include?(1)

h.key?(1)

h.member?(1)

h.has_value?("three")

h.value?("three")

h.empty?

h.size

如果h有键为1,则为真

h.has_key? 的同义词

h.has_key? 的同义词

h.has_key? 的同义词

只要 h 有值为 “three”,就为真

has_value? 的同义词

如果h没有任何键-值对,就为真

h中键-值对个数

8. Ruby 和 Rails 方法调用中的散列

<% def mini_link_to(text, specs)

target = "/#{specs[:controller]}/#{specs[:action]}/#{specs[:id]}"

return "<a href=\"#{target}\">#{text}</a>"

end

%>

<%= mini_link_to "Click here",

:controller => "work",

:action => "show",

:id => 1

%>

将这段代码保存到文件 minilink.erb 中,然后使用 ERb 来运行它

$ erb minilink.erb



ERb会填充模板,结果如下所示:

<a href="/work/show/1">Click here</a>



四、集合的中心:Enumerable 模块

1. 数组和散列都混含了 Enumerable 模块

class c

include Enumerable

def each

# relevant code here

end

end



2. 通过 each 获得可枚举性

each 的任务就是通过匿名调用将所有的条目传递给一个给定的代码块,每次只传一个。

find、select、reject、map、any? 和 all? 它们的工作方式都调用方法 each:

class Rainbow

include Enumerable

def each

yield "red"

yield "orange"

yield "yellow"

yield "green"

yield "blue"

yield "indigo"

yield "violet"

end

end

r = Rainbow.new

y_color = r.find {|color| color[0,1] == 'y' }

puts "First color starting with 'y' is #{y_color}."

可以重写为:

class Rainbow

COLORS = ["red", "orange", "yellow", "green",

"blue",
"indigo", "violet"]

def each

COLORS.each {|color| yield color }

end

end



3. 可枚举的字符串

(1)使用 String#each 进行迭代

s = "This is\na multiline\nstring."

s.each {|e| puts "Next value: #{e}" }

这段代码将一个多行字符串(带显式嵌入的换行符\n)赋值给一个变量,然后对这个字符串进行迭代

Next value: This is

Next value: a multiline

Next value: string.



(2)使用 each_byte 进行字符迭代

"abc".each_byte {|b| puts "Next byte: #{b}" }



输出字符的 ASCII 码,如果想把它们转换成字符,可以调用数值的 chr 方法

"abc".each_byte {|b| puts "Next character: #{b.chr}" }



直接对字符进行迭代,创建 String.each_char 方法

class String

def each_char

each_byte { |b| yield b.chr}

end

end



五、集合排序

1. 数组排序

>> [3,2,5,4,1].sort

=> [1, 2, 3, 4, 5]



2. 对象数组排序

[ed1,ed2,ed3,ed4,ed5].sort

在 Edition 类中定义<=>方法:

def <=>(other_edition)

self.price <=> other_edition.price

end

end



3. 在代码块中定义排序顺序

year_sort = [ed1,ed2,ed3,ed4,ed5].sort do |a,b|

a.year <=> b.year

end



使用 sort_by 的简洁排序,和 sort 一样,sort_by 也是 Enumerable 的一个实例方法。它们的主要区别是:必须为 sort_by 提供一个代码块,代码块中只需要说明如何对集合中的单个条目进行处理

>> ["2",1,5,"3",4,"6"].sort_by {|a| a.to_i }

=> [1, "2", "3", 4, 5, "6"]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: