やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

組込ライブラリ(Array)

 Objectに次いで超大事。

成果物

情報源

Array

宣言・代入

a = [1,2,3]
a = Array.new(3) {|i| i*2} #=> [0, 2, 4]

fill

 一括代入する。

a = [1,2,3]
a.fill(9) #=> [9,9,9]
a = [0,1,2,3,4,5,6,7,8,9]
a.fill('x', 5..7) #=> [0, 1, 2, 3, 4, "x", "x", "x", 8, 9]
a = [0,1,2,3,4,5,6,7,8,9]
a.fill(5, 3) {|i| 'x'} #=> [0, 1, 2, 3, 4, "x", "x", "x", 8, 9]
a = [0,1,2,3,4,5,6,7,8,9]
a.fill(5..7) {|i| 'x'} #=> [0, 1, 2, 3, 4, "x", "x", "x", 8, 9]

each/each_index, cycle

 ループする。

要素

[1,2,3].each {|v| p v}

インデックス

a = %w(a b c)
a.each_index {|i| p "#{i}:#{a[i]}"} #=> 0:a 1:b 2:c

 cycleで無限ループや指定回数ループできる。

[1,2,3].cycle {|v| p v} # 1,2,3,1,2,3,...
[1,2,3].cycle(2){|v| p v} # 1,2,3,1,2,3

演算子

[], []=

a = [1,2,3]
a[0]  #=> 1
a[9]  #=> nil
a[-1] #=> 3

 範囲外エラーにならずnilになるだと?! fetchを使えばIndexErrorにできる。

a = [1,2,3]
a[0] = 9
p a #=> [9, 2, 3]
a[9] = 99
p a #=> [9, 2, 3, nil, nil, nil, nil, nil, nil, 99]

 範囲外エラーにならずそれより前の要素を生成してnilで初期化するだと?!

 バグりそうで怖い。

範囲

a = [0,1,2,3,4,5,6,7,8,9]
a[2..4]    #=> [2, 3, 4]
a[7..99]   #=> [7, 8, 9]
a[99..100] #=> nil
a[99..-1]  #=> nil
a[-3..-1]  #=> [7, 8, 9]
a[-1..-3]  #=> []

長さ

a = [0,1,2,3,4,5,6,7,8,9]
a[2, 2]  #=> [2, 3]
a[5, 3]  #=> [5, 6, 7]
a[5, -1] #=> nil

 takeで先頭からn個の要素配列を返す。

a = [0,1,2,3,4,5,6,7,8,9]
a.take(3) #=> [0,1,2]

&

 積集合。

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

 &intersectionの糖衣構文である。

[1,2,3,4,5].intersection([1,3]) # => [1, 3]

|

 和集合。

[1,2,3] | [0,1,4] #=> [1, 2, 3, 0, 4]

 |unionの糖衣構文である。

[1,2,3].union([0,1,4]) #=> [1, 2, 3, 0, 4]

+

[1,2] + [2,3] #=> [1, 2, 2, 3]

 +concatの糖衣構文である。

a = [1,2]
a.concat([2,3])
p a #=> [1, 2, 2, 3]

-

[1,2,3] - [2] #=> [1,3]

 -differenceの糖衣構文である。ただし非破壊的。

a = [1,2,3]
a.difference([2]) #=> [1,3]
p a #=> [1, 2, 3]

*

 同じ要素をくり返して追加する。

[1,2,3] * 2 #=> [1,2,3,1,2,3]

 文字列連結する。

[1,2,3] * ',' #=> "1,2,3"

 上記はjoinの糖衣構文である。

[1,2,3].join ',' #=> "1,2,3"

<<

 要素を追加する。

a = [1]
a << 2
p a #=> [1,2]

<=>

 比較結果を返す。

[1,2,3] <=> [1,3,2] #=> -1  左 < 右
[1,2,3] <=> [1,2,3] #=> 0   左 == 右
[1,2,3] <=> [1,2]   #=> 1   左 > 右
[1,2,3] <=> ['1']   #=> nil 比較不可

==

 全要素が等しければ真を返す。

[1,2,3] == [1,2,3] #=> true
[1,2,3] == [1,2]   #=> false

判定

all?

 全要素が条件を満たしていたら真を返す。

[true,true].all?  #=> true
[true,false].all? #=> false
[0,2,4].all? {|v| 0 == (v % 2)} #=> true
[1,2,3].all? {|v| 0 == (v % 2)} #=> false
%w(abc def ghi).all?(/[a-z]+/) #=> true
%w(abc 123 ghi).all?(/[a-z]+/) #=> false

any?

 全要素のうちひとつでも条件を満たしていたら真を返す。

[true,false].any? #=> true
[false,false].any? #=> false
[0,3,5].any? {|v| 0 == (v % 2)} #=> true
[1,3,5].any? {|v| 0 == (v % 2)} #=> false
%w(abc 123 456).any?(/[a-z]+/) #=> true
%w(123 456 789).any?(/[a-z]+/) #=> false

empty?

[].empty?  #=> true
[1].empty? #=> false

include?

 指定した要素を持っているとき真を返す。

a = %w(a b c)
a.include?('b') #=> true
a.include?('z') #=> false

none?

 全要素が偽なら真を返す。!all?と同じ。

[false,false].none? #=> true
[true,false].none? #=> false
[1,3,5].none? {|v| 0 == (v % 2)} #=> true
[0,3,5].none? {|v| 0 == (v % 2)} #=> false
%w(123 456 789).none?(/[a-z]+/) #=> true
%w(123 def ghi).none?(/[a-z]+/) #=> false

one?

 全要素のうち丁度ひとつだけ真なら真を返す。

[true,false,false].one? #=> true
[true,true,false].one? #=> false
[0,3,5].one? {|v| 0 == (v % 2)} #=> true
[0,2,5].one? {|v| 0 == (v % 2)} #=> false
%w(abc 123 456).one?(/[a-z]+/) #=> true
%w(abc def 789).one?(/[a-z]+/) #=> false

clear, delete/delete_at/delete_if/reject!, drop/drop_while

 clearは全要素を削除する。

a = [1,2,3]
a.clear
p a #=> []

 deleteは指定した値と一致する要素を削除する。

a = %w(a b c d a b)
a.delete 'a'
p a #=> ["b", "c", "d", "b"]

 delete_atは指定した位置の要素を削除して返す。

a = %w(a b c)
a.delete_at 0 #=> "a"
p a #=> ["b", "c"]

 delete_ifは条件に一致した要素を削除する。reject!も同様。

a = [0,1,2,3]
a.delete_if {|v| 0 == (v % 2)}
p a #=> [1,3]
a = [0,1,2,3]
a.reject! {|v| 0 == (v % 2)}
p a #=> [1,3]

 dropは指定した数だけの要素を先頭から順に捨てて残った要素の配列を返す。非破壊的。

a = [0,1,2,3,4,5,6,7,8,9]
a.drop(3) #=> [3, 4, 5, 6, 7, 8, 9]
p a #=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 drop_whileは最初に偽となった要素の手前まで捨て、残った要素の配列を返す。非破壊的。

a = [0,1,2,3,4,5,6,7,8,9]
a.drop_while {|v| v < 3} #=> [3, 4, 5, 6, 7, 8, 9]
p a #=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

append/push, pop/shift

 append/pushは末尾に追加。pop/shiftは末尾から取り除いて返す。

a = [0,1]
a.append 2 #=> [0,1,2]
a.push 3   #=> [0,1,2,3]
p a #=> [0,1,2,3]
p a.pop #=> 3
p a #=> [0,1,2]

prepend,unshift

 要素を先頭に追加する。

a = [1,2]
a.prepend 0
p a #=> [0,1,2]
a = [1,2]
a.unshift 0
p a #=> [0,1,2]

insert

 指定した位置に指定した要素を追加する。

a = [0, 1, 2]
a.insert 1, 'x'
p a #=> [0, "x", 1, 2]
a = [0, 1, 2]
a.insert 1, 'x', 'y'
p a #=> [0, "x", "y", 1, 2]

select,filter/keep_if

 select,filterは条件に一致した要素の配列を返す。

[0,1,2,3].select {|v| v.even? } #=> [0,2]
[0,1,2,3].filter {|v| v.even? } #=> [0,2]

 select!,filter!は上記の破壊的メソッド版。

 keep_ifはブロックがfalseを返した要素を削除する。破壊的。select!と同じだが削除対象がなければ変更しない。

a = %w(a b c d e f)
a.keep_if {|v| v =~ /[aiueo]/} # => ["a", "e"]
p a # => ["a", "e"]

sum

 要素の合計値を返す。

[1,2,3].sum #=> 6

inject,reduce/reject

 injectは畳み込み演算する。

# 合計
p [1, 2, 3, 4, 5].inject {|result, item| result + item } #=> 15
# 自乗和
p [1, 2, 3, 4, 5].inject(0) {|result, item| result + item**2 }  #=> 55

 以下のようにも書ける。

result = 0
[1, 2, 3, 4, 5].each {|v| result += v }
p result # => 15

p [1, 2, 3, 4, 5].inject(:+)                    #=> 15
p ["b", "c", "d"].inject("abbccddde", :squeeze) #=> "abcde"

文字が複数並んでいたら 1 文字にまとめます。

 rejectselectの否定形。条件が偽の要素でできた配列を返す。

[1, 2, 3, 4, 5].reject {|i| i.even? }  # => [1, 3, 5]

 rejectinjectの逆かと思ったが違った。

map,collect

 各要素に対してブロックを評価した結果をすべて含む配列を返す。

[1, 2, 3].map {|v| v*2} #=> [2, 4, 6]
[1, 2, 3].collect {|v| v*2} #=> [2, 4, 6]

combination, permutation

 組合せと順列。

 combinationはサイズ n の組み合わせをすべて生成する。

a = [1, 2, 3, 4]
a.combination(1).to_a  #=> [[1],[2],[3],[4]]
a.combination(2).to_a  #=> [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
a.combination(3).to_a  #=> [[1,2,3],[1,2,4],[1,3,4],[2,3,4]]
a.combination(4).to_a  #=> [[1,2,3,4]]
a.combination(0).to_a  #=> [[]]: one combination of length 0
a.combination(5).to_a  #=> []  : no combinations of length 5

 permutationはサイズ n の順列をすべて生成する。

a = [1, 2, 3]
a.permutation.to_a     #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
a.permutation(1).to_a  #=> [[1],[2],[3]]
a.permutation(2).to_a  #=> [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]]
a.permutation(3).to_a  #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
a.permutation(0).to_a  #=> [[]]: one permutation of length 0
a.permutation(4).to_a  #=> []  : no permutations of length 4

 重複を許すときはrepeated_*メソッドを使う。

a = [1, 2, 3, 4]
a.repeated_combination(4).to_a
a = [1, 2, 3]
a.repeated_permutation(3).to_a

product

 配列同士の組合せを返す。

[1,2,3].product([4,5]) #=> [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]]
%w(♠ ♥ ♦ ♣).product(%w(A 2 3 4 5 6 7 8 9 10 J Q K))

compact/compact!

 要素からnilをすべて削除する。

a = [1, nil, 2, nil, 3, nil]
p a.compact   #=> [1, 2, 3]
p a           #=> [1, nil, 2, nil, 3, nil]
a.compact!
p a           #=> [1, 2, 3]
p a.compact!  #=> nil

count, length, size

 要素数を返す。

p [0,1,2].count #=> 3
p [0,1,2].count {|v| v.even?} #=> 2
p [0,1,2,1,1].count 1 #=> 3

 length,sizeは引数なし。

p [0,1,2].size   #=> 3
p [0,1,2].length #=> 3

max/min, minmax

[3,1,5,2].max #=> 5
[3,1,5,2].min #=> 1
[3,1,5,2].max 3 #=> [5,3,2]
[3,1,5,2].min 3 #=> [1,2,3]
ary = %w(albatross dog horse)
ary.max {|a, b| a.length <=> b.length }    #=> "albatross"
ary.max(2) {|a, b| a.length <=> b.length } #=> ["albatross", "horse"]

 最小と最大を返す。

any = %w(albatross dog horse)
any.minmax                                 #=> ["albatross", "horse"]
any.minmax{|a,b| a.length <=> b.length }   #=> ["dog", "albatross"]
[].minmax # => [nil, nil]

sample

 要素をひとつランダム選択して返す。

 サイコロは以下。

(1..6).to_a.sample #=> 4

sort, sort_by, reverse, shuffle, rotate

a = [3,1,4,2]
p a.sort
p a
a.sort!
p a
p a.reverse
a.reverse!
p a
p a.shuffle
a.shuffle!
p a
p a.rotate
a.rotate!
p a

 sort!<=>で比較するが、sort_by!は比較する要素を指定する。sort_by!は破壊的メソッドしかない。

  • sort! {|a, b| ... }
  • sort_by! {|item| ... }
ary1 = [ "d", "a", "e", "c", "b" ]
p ary1.sort                             #=> ["a", "b", "c", "d", "e"]

ary2 = ["9", "7", "10", "11", "8"]
p ary2.sort                             #=> ["10", "11", "7", "8", "9"] (文字列としてソートするとこうなる)
p ary2.sort{|a, b| a.to_i <=> b.to_i }  #=> ["7", "8", "9", "10", "11"] (ブロックを使って数字としてソート)

# sort_by を使っても良い
p ary2.sort_by{|x| x.to_i }             #=> ["7", "8", "9", "10", "11"]
fruits = %w{apple pear fig}
fruits.sort_by! { |word| word.length }
fruits # => ["fig", "pear", "apple"]

uniq/uniq!

 重複要素を削除する。

[1,1,3,2,2,1].uniq #=> [1,3,2]

transpose

 行列とみたてて行と列を入れ替える。

p [[1,2],
   [3,4],
   [5,6]].transpose
# => [[1, 3, 5], [2, 4, 6]]

zip

 各配列の同じ位置にある要素同士でできた新たな配列を返す。

p [1,2,3].zip([4,5,6], [7,8,9]) # => [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

所感

 ほかにもネストした配列を参照・操作するdig,flattenなど面白いメソッドがある。詳細はArray参照。

対象環境

$ uname -a
Linux raspberrypi 5.10.52-v7l+ #1441 SMP Tue Aug 3 18:11:56 BST 2021 armv7l GNU/Linux