Ruby中的数组是一个容器,数组中的每一个元素都是一个对象的引用。html
注意,Array类中包含了Enumerable模块,因此Enumerable中的方法也都能使用,例如Enumerable中的reduce()方法也是很是好用的方法。python
# 1.使用[xxx]方式建立 arr1 = ["Perl", "Python", "Ruby"] # 2.空数组 arr = [] # 3.使用%w或%W能够省略引号和逗号,而使用空格分隔 # %w(小写)不解释内插表达式,%W(大写)解释内插 # 此时中括号能够换成其它符号,如%{}、%<>、%## # 若是元素中要保留空格,使用反斜线转义 arr2 = %w[Perl Python Ruby] # 3个元素 arr3 = %w<Perl Python\ Ruby> # 2个元素 a = "Perl" arr4 = %w(#{a} Python Ruby) # #{a}不作变量替换,第一个元素为`\#{a}` arr5 = %W(#{a} Python Ruby) # #{a}会作变量替换 # 4.使用%i建立符号symbol数组 arr6 = %i(Perl Python Ruby) # [:Perl, :Python, :Ruby]
建立arr时,容许解析表达式。例如,范围表达式、变量等。数组
[-10...0, 0..10] a=10;b=20 c=[a, b]
Array类的new()方法能够建立数组。ruby
ary = Array.new #=> [] Array.new(3) #=> [nil, nil, nil] Array.new(3, true) #=> [true, true, true]
当new()指定了第二个参数时,各初始化的元素将指向同一个对象。app
arr=Array.new(3, "abc") arr[1][0]="A" #=> ["Abc", "Abc", "Abc"]
因此,建议初始化的元素采用不可变对象,如数值、symbol、bool的true和false。或者使用下面这种块结构的初始化方式。函数
能够将块结构接管Array.new的第二个默认值参数,它将每一个元素的索引值传递到块变量中。例如:测试
Array.new(4) {Hash.new} #=> [{}, {}, {}, {}] Array.new(4) {|i| i.to_s } #=> ["0", "1", "2", "3"]
例如,初始化互不影响的3个"abc"元素数组。fetch
arr = Array.new(3) {"abc"} arr[0][1]="A" p arr # ["aAc", "abc", "abc"]
多维数组:code
Array.new(3){Array.new(2)} #=> [[nil, nil], [nil, nil], [nil, nil]]
经过Kernel模块提供的Array(arg)
方法建立数组。htm
该方法将给定参数arg转换成数组。
Array({:a => "a", :b => "b"}) #=> [[:a, "a"], [:b, "b"]] Array(1..5) #=> [1, 2, 3, 4, 5]
若是某个类定义了to_a()方法,能够将对象转换成数组结构。例如,hash类有to_a()方法,因此能够将hash结构转换成数组。
my_hash = {Perl: "Larry Wall", Ruby: "Matz"} arr = my_hash.to_a() # [[:Perl, "Larry Wall"],[:Ruby, "Matz"]]
字符串对象有split()方法,能够按照指定的分隔符将字符串切割成数组。
p "perl python ruby".split # ["perl", "python", "ruby"]
获取数组的方式有多种。列出了如下几种常见的(返回多个元素的方式将以新数组的方式返回):
其中,at()方法和slice()方法不多用,由于arr[]
的方式已经足够。
若是索引越界,则返回nil。可是有一个特殊状况,当使用slice的操做时,由于要返回的是数组,因此有以下"异常"状况:
a = %w(a b c d e) a[4] #=>"e" a[4,0] #=>[] a[4,1] #=>["e"] a[4..100] #=>["e"] a[5] #=>nil a[5,0] #=>[] a[5,1] #=>[] a[5..100] #=>[] a[6] #=>nil a[6,0] #=>nil a[6,1] #=>nil
上面a[5]返回nil,但a[5,x]返回空数组,而a[6]以及a[6,x]则直接返回nil。首先a[6]以及a[6,x]都是索引越界,因此返回nil。而a[5]的5是索引越界,因此返回nil。问题是a[5,x]返回的是空数组,而不是nil。这是由于a[5,x]是一个slice操做,它要求返回新数组。从下面的slice操做能够理解为何a[5,1]返回空数组。
p a[0, 5] # ["a", "b", "c", "d", "e"] p a[1, 4] # ["b", "c", "d", "e"] p a[2, 3] # ["c", "d", "e"] p a[3, 2] # ["d", "e"] p a[4, 1] # ["e"] p a[5, 0] # [] p a[5, 1] # []
也就是说,对于slice操做,arr[len, x]的arr[len]能够认为是最边缘的元素,尽管arr[len]已经越界了。
其实slice()
是按规则删除元素,只不过它不影响原始数组。而slice!()
则是删除一些元素并直接影响原始数组,也就是在原处修改数组。
a = [1, 2, 3, 4] a.slice!(1,2) # 返回:[2, 3],a变成[1, 4]
Array类提供了values_at()方法,能够取得每一个数组对象中指定索引处的多个分散元素。
例如:
a = %w(a b c d e) p a.values_at(1) # ["b"] p a.values_at(0, 0, 2, 4) # ["a", "a", "c", "e"] p a.values_at(1, 2, 10) # ["b", "c", nil] p a.values_at(1..3) # ["b", "c", "d"]
除了这种方式能够取得分散元素,还可使用map()函数进行操做。例如,等价于values_at(0,0,2,4,10)
的写法为:
p [0, 0, 2, 4, 10].map{|i| a[i]}
好比,默认状况下索引越界时将返回nil,使用fetch()方法能够获取指定索引的元素,且能够指定越界时的默认值,不然直接报错。
a=%w(a b c d e) p a.fetch(10) # 报错:索引越界IndexError p a.fetch(10, "ten") # 指定默认值
使用first()和last()能够取得数组中的第一个元素和最后一个元素,它们会取得元素后当即退出。
a = %w(a b c d e) p a.first # "a" p a.last # "e"
使用take()能够取得数组中的前n个元素,使用drop()能够取得除了数组前n个元素外剩下的元素。它们都不会删除元素。
a = %w(a b c d e f) p a.take(2) # ["a", "b"] p a.drop(2) # ["c", "d", "e", "f"] p a # ["a", "b", "c", "d", "e", "f"]
[] at slice
方法均可觉得数组中指定位置处元素赋值。惟一须要注意的是范围赋值操做,将其认为是将范围内的值设置为新值。
a = %w(a b c d e) # 单元素赋值 a[1] = "bb" # 为第2个元素赋值 # 范围赋值 a[1, 2] = ["bb", "cc"] # a=%w(a bb cc d e) a[1, 2] = %w(bb) # a=%w(a bb d e) a[1, 2] = %w(bb cc dd) # a=%w(a bb cc dd d e) # 彻底插入元素:指定len为0 a[1, 0] = %w(B C) # a=%w(a B C b c d e) a[1..1] = %w[B C] # 与上式等价 # 彻底删除元素:将右边对象设置为空数组 a[1, 2] = [] # a=%w(a d e)
赋值语句的返回值是所赋值内容。例如a[1,0] = %w[B C]
的返回值是%w[B C]
,于此同时原始数组被修改。
数组能够执行+ - * & |
操做,不过- & |
是将数组做为集合进行操做的,相关内容见后文对应小节。而使用+
和*
能够扩展数组。
由于ruby中的一元运算符操做x += y
和二元运算符操做x = x + y
彻底等价,都会建立新对象x。因此,涉及到大数组时,可能效率会比较低下。
+
能够将两数组加在一块儿返回一个新数组:
arr = [1, 2, 3] arr1 = arr + [3, 4, 5] # arr不变,arr1=[1,2,3,3,4,5]
concat()
方法也能将0到多个数组扩展到一个数组上。注意,concat()会将参数数组全都"压平"而后追加到原数组尾部:
["a", "b"].concat(["c", "d"]) # ["a", "b", "c", "d"] ["a"].concat(["b"], ["c", "d"]) # ["a", "b", "c", "d"] ["a"].concat # ["a"] a = [1, 2, 3] a.concat([4, 5]) # a=[1, 2, 3, 4, 5] a = [1, 2] a.concat(a, a) # a=[1, 2, 1, 2, 1, 2]
*
对于数组有两种用法:
# 1.数组乘一个数值对象,返回新数组 # 数值必须在数组后面,不能放前面 arr = [1, 2, 3] arr1 = arr * 2 # arr不变,arr1=[1,2,3,1,2,3] # 2.数组乘一个字符串,将返回字符串而非新数组 arr = [1, 2, 3] arr1 = arr * "," # arr不变,arr1="1,2,3" arr2 = arr * ",+" # arr不变,arr2="1,+2,+3"
获取数组长度,可使用count()、length()或size()方法,后二者等价。而count()方法经过参数或语句块的方式还能获取知足条件的元素个数。
a = %w(a b c d e) p a.length # 5 p a.count # 5 p a.size # 5 ary = [1, 2, 4, 2] ary.count # 4,数组元素个数 ary.count(2) # 2,等于2的元素个数 ary.count {|x| x%2 == 0} # 3,偶元素个数
检查数组是否为空数组,使用empty?()
方法:
p a.empty? # false
检查数组是否包含某元素,使用include?()
方法:
p a.include?('c') # true p a.include?('x') # false
push()/append()、unshift()、insert()或特殊符号<<
,注意它们都是原处修改数组对象的:
# push()向尾部插入元素 # push()返回数组自身,能够链式插入 # append()等价于push() arr = [1, 2, 3, 4] arr.push(5) # arr = [1, 2, 3, 4, 5] arr.push(6).push(7) # arr = [1,2,3,4,5,6,7] # <<向尾部插入元素 # <<返回数组自身,因此能够进行链式插入 arr = [1, 2, 3, 4] arr << 5 # arr = [1, 2, 3, 4, 5] arr << 6 << 7 # arr = [1,2,3,4,5,6,7] arr <<8 <<[9, 10] # [1,2,3,4,5,6,7,8,[9,10]] # unshift()向头部插入元素 arr = [1, 2, 3, 4] arr.unshift(0) # arr = [0, 1, 2, 3, 4] # insert()向给定索引位置处插入元素,可一次性插入多个 arr = [0, 1, 2, 3, 4] arr.insert(3, "Ruby") # [0, 1, 2, 'Ruby', 3, 4] arr.insert(3,"Perl","Shell") # # [0,1,2,'Perl','Shell','Ruby',3,4] # 经过范围赋值的方式插入元素 arr = [0, 1, 2, 3, 4] arr[1,0] = [11, 22] # [0, 11, 22, 1, 2, 3, 4] arr[1..1] = [11, 22] # 与上等价
按位置删除元素
pop()、shift()、delete_at():
# pop()移除数组尾部元素并返回该元素 arr = [1, 2, 3, 4, 5, 6] arr.pop # 返回6, arr变成[1, 2, 3, 4, 5] # shift()从数组头部移除一个元素并返回该元素 arr.shift # 返回1,arr变成[2, 3, 4, 5] # delete_at()移除给定索引位置处的元素并返回该元素 arr.delete_at(2) # 返回4,arr变成[2, 3, 5] # 经过范围赋值的方式删除元素 arr = [1, 2, 3, 4, 5, 6] arr[1, 2] = [] # arr = [1, 4, 5, 6]
按给定值删除元素
delete():
# delete()删除数组中等于某个值的元素 arr1=[1, 2, 2, 4] arr2 = %w(Perl Shell Python Ruby) arr1.delete(2) # 返回2,arr1变成[1,4] arr2.delete "Shell" # 返回"Shell",arr2变成%w(Perl Python Ruby)
删除重复元素
uniq()、compact()以及成对的带感叹号后缀的uniq!()、compact!():
# uniq()删除重复元素,不是原处修改对象的 arr=%w(a b c x y a c b y) uniq_arr = arr.uniq # uniq_arr=%w(a b c x y),arr不变 # uniq!()删除重复元素,直接修改原始对象且返回修改后的数组 arr=%w(a b c x y a c b y) uniq_arr = arr.uniq! # uniq_arr和arr都变成%w(a b c x y)
# compact()删除全部nil元素,压缩数组,不是原处修改对象 arr = ["a", "b", "c", nil, "a", "c", nil, "b"] compact_arr = arr.compact p compact_arr # ["a", "b", "c", "a", "c", "b"] p arr # ["a","b","c",nil,"a","c",nil,"b"] # compact!()删除全部nil元素,压缩数组,原处修改对象 arr = ["a", "b", "c", nil, "a", "c", nil, "b"] compact_arr = arr.compact! p compact_arr # ["a", "b", "c", "a", "c", "b"] p arr # ["a", "b", "c", "a", "c", "b"]
清空数组
clear()直接清空数组:
a = [1,2,3] a.clear() # a=[]
迭代方式有不少,经过for、while、times、数组自带的each、each_index、reverse_each,还有Mix-in Enumerator后获取的迭代方式:each_cons、each_slice、each_entry、each_with_index、with_index、each_with_object等。
此处仅简单介绍for/while/times迭代和数组自身几个迭代方法,关于从Enumerator获取的相关的迭代方式,参见对应文章:Enumerator各类迭代方式。
数组自身迭代方法:
# for x = %w(a b c d e) for i in x puts "element: #{i}" end # while x = %w(a b c d e) i=0 while i < x.length puts "element: #{x[i]}" i += 1 end # times x = %w(a b c d e) x.length.times do |n| puts "element: #{x[n]}" end
each {|item| block} → ary each → Enumerator
迭代数组中每一个元素,并将每一个元素传递给语句块中的变量。最后返回数组自身。
# each迭代数组,不会修改原始数组 arr = [1, 2, 3, 4, 5] arr.each {|a| print a -= 10, " "} ## 输出:-9 -8 -7 -6 -5 ## arr=[1, 2, 3, 4, 5]
each_index {|index| block} → ary each_index → Enumerator
迭代数组中的每一个元素,并将每一个元素的索引传递给语句块中的变量。最后返回数组自身。
a = ["a", "b", "c", "d"] a.each_index do |x| puts "index: #{x}" end ## 输出结果: =begin index: 0 index: 1 index: 2 index: 3 =end
# reverse_each反序迭代数组 arr = [1, 2, 3, 4, 5] arr.reverse_each {|a| print "#{a}-"} ## 输出5-4-3-2-1-
类方法:
# try_convert(arg)将转换成数组 arr = Array.try_convert([1]) # [1] arr1 = Array.try_convert(1) # nil arr2 = Array.try_convert("1") # nil # 测试元素是不是数组、字符串 if tmp=Array.try_convert(arg) # arg is an array elsif tmp = String.try_convert(arg) # arg is a string end
# to_a()和to_ary(),都返回self # 表示返回当前数组的引用 # 对于数组自身来讲,这二者没差异 arr = ["foo", "bar"] arr1 = arr.to_a p arr.eql?(arr1) # true p arr.object_id # 42285320 p arr1.object_id # 42285320
# to_s()等价于inspect() # 将数组转换成字符串 arr = ["foo", "bar"] arr.to_s # [\"foo\", \"bar\"]
# to_h()将数组转换成hash h1 = [["foo",1], ["bar", 2]].to_h # to_h的语句块形式,每一个元素都将做为块中的变量 h2 = [["foo", 1],["bar", 2]].to_h {|x| x} h3 = ["foo", "bar"].to_h {|s| [s, s * 2] } p h1 # {"foo"=>1, "bar"=>2} p h2 # {"foo"=>1, "bar"=>2} p h3 # {"foo"=>"foofoo", "bar"=>"barbar"}
四种比较方式:等值比较==
、hash值比较eql?
、大小比较<=>
和是否同一数组对象的比较equal?()
。
数组还继承了Object类的===
,对于数组而言,它等价于==
。
只有两数组长度相同、两数组对应索引位置的元素使用==
测试也相等,才断定两数组相等。
返回true/false。注意,若是含有nil元素,则nil与nil的比较结果为true。
[ "a", "c" ] == [ "a", "c", 7 ] # false [ "a", "c", 7 ] == [ "a", "d", "f" ] # false [ "a", "c", 7 ] == [ "a", "c", 7 ] # true ["a", 1, 1.2] == ["a", 1.0, 1.2] # true
只有两数组对象的内容彻底相同时返回true,不然返回false。
它是根据各对象计算出的hash值比较的,通常来讲比==
要严格一点。
[1, 2] == [1, 2.0] # true [1, 2].eql?([1, 2.0]) # false [1, 2].eql?([1, 2]) # true [1, 2].hash # 2027605168499122706 [1, 2.0].hash # 3393919734812826294
继承自Object类,它比较的是二者是不是同一对象。这个方法基本上不会被子类重写。
这个方法比==
和eql?()
都要严格。
[1,2].equal?([1,2]) # false a = [1, 2] b = a a.equal?(b) # true
这个比较符号,当执行的是obj1 <=> obj2
时:
对于数组而言,对数组中的每一个元素都一一对应的比较,直到找到某个数组中的元素不等于另外一个数组中对应索引处的元素就返回结果。若是数组长短不一致,且较短数组的全部元素都和另外一数组对应元素相等,则长数组更大。因而能够推断,只有两数组长度、内容彻底相等(经过==
号比较,例如1等于1.0),数组才相等。
arr = [1, 3, 5, 7] arr1 = [1, 3.0, 5, 7] arr2 = [1, 3, 5] arr3 = [1, 3, 5, 7, 9] arr4 = [1, 3, 7, 5] arr5 = [1, 3, 7, 5, nil] p arr <=> arr1 # 0 p arr <=> arr2 # 1 p arr <=> arr3 # -1 p arr <=> arr4 # -1 p arr <=> arr5 # -1