よしやるかー。先に言っておくと Arrays だけで力尽きた。
## Ch. 7 Arrays
- 32-bit index なので index の範囲は 0 から 4,294,967,294 まで
> let a = [] undefined > a[4294967294] = 1 1 > a.length 4294967295 > a[4294967295] = 2 2 > a.length 4294967295 > a [ <4294967294 empty items>, 1, '4294967295': 2 ]
へー。4294967295はキーになっちゃったし length も変わらないのか。面白いな。
### 7.1 Creating Arrays
Array Literal []
- カンマだけ書いて値を書かなかったら間の要素が抜けた sparse array になる。その要素を参照しようとすると
undefined
になる
> let count = [1,,3] undefined > count [ 1, <1 empty item>, 3 ] > count[1] undefined
- ちなみに Array literal は末尾のカンマを許可してるので
[,,]
の length は3じゃなくて2 - ES6 以降では spread operator が利用できる
> let a = [1,2,3]; undefined > [0, ...a, 4] [ 0, 1, 2, 3, 4 ]
- spread operator は iterable なオブジェクトに対して利用できる。ので、文字列を渡すとこうなる:
> [..."abcde"] [ 'a', 'b', 'c', 'd', 'e' ]
new Array()
, Array.of()
, Array.from()
- 配列の生成には
Array()
コンストラクターも使用できる。けど、引数の数で動きが変わるのが危なそうなので、使わないだろうな。 new Array()
はそんな感じなので ES6 でArray.of()
が導入された。けどリテラルで良さそう。- ES6 で
Array.from()
も導入された。これは iterable や array-like なオブジェクトから配列を生成できる。- iterable オブジェクトの場合は、これと同じ
[...iterable]
- array-like オブジェクトの場合は array-like から本当の array を作成するのに便利(array-like オブジェクトは、Array じゃないけど数値の
length
プロパティがあって、その長さ分の数値のプロパティを持ってるオブジェクトのこと) - オプショナルで2つ目の引数として関数を渡すことができて要素に変換をかけることができる。
map
みたいな感じ。
- iterable オブジェクトの場合は、これと同じ
### 7.2 Reading and Writing Array Elements
- 232-1 より小さい非負の整数のプロパティ名を指定したら、自動的に
length
プロパティが更新される。あー、これが今日のはじめでlength
が更新されなかったやつか。
### 7.4 Array Length
- 現在の
length
の値以上の index に値を設定した場合にはlength
がその index + 1 の値に更新される length
に現在の値よりも小さな値(非負)を設定したら、その値以上の要素は削除される
### 7.6 Iterating Arrays
- ES6以降では for/of ループで処理を行うことができる
> for(const v of [..."Hello World"]) { ... console.log(v); ... } H e l l o W o r l d
- index を使用したい場合には
entries()
を使う
> for(const [i, v] of [..."Hello World"].entries()) { ... console.log(`${i}:${v}`); ... } 0:H 1:e 2:l 3:l 4:o 5: 6:W 7:o 8:r 9:l 10:d
forEach()
もある
> let uppercase = ""; undefined > [..."Hello World"].forEach(c => uppercase += c.toUpperCase()) undefined > uppercase 'HELLO WORLD'
forEach()
は第二引数で index を受け取ることもできる
> [..."Hello World"].forEach((v,i) => console.log(`${i}:${v}`)) 0:H 1:e 2:l 3:l 4:o 5: 6:W 7:o 8:r 9:l 10:d
forEach()
はfor/of
ループと違って sparse array の存在しない要素に対しては呼び出されない
// for/of > for(const v of [1,,,4]){ ... console.log(v); ... } 1 undefined undefined 4 // forEach() > [1,,,4].forEach(v => console.log(v)); 1 4
ん? undefined
を要素として入れたらどうなるんだろう?
> [1,2,undefined,4].forEach(v => console.log(v)); 1 2 undefined 4
へー。その場合は処理されるのか。 sparse array の要素がないことの表現としての undefined
と実際に要素として設定された undefined
は区別されてるんだね。
- あとは普通の for loop ももちろん使用可能
### 7.8 Array Methods
forEach()
- 途中でイテレーションを終了させる方法はない。つまり普通の
for
ループのbreak
に相当する仕組みはない。 - さっきも書いたけど sparse array の存在しない要素に対しては呼び出されない
map()
- sparse array の場合は存在しない要素に対しては呼び出されないが、返される配列も sparse になる。
> [1,,,4].map(v => v*2); [ 2, <2 empty items>, 8 ]
filter()
- sparse array の存在しない要素はスキップされる
> [1,,,4].filter(v => v > 1) [ 4 ]
- なので、 sparse array の隙間をなくしたかったらこんな感じにかける:
> [1,,,4].filter(() => true) [ 1, 4 ]
find()
findIndex()
- 最初にみつかった要素、またはインデックスを返す
- 見つからなかった場合、
find()
はundefined
をfindIndex()
は-1
を返す
every()
some()
every()
は全ての要素が条件を満たした場合にtrue
を、それ以外の場合はfalse
を返す。ひとつでも条件を満たさないものがあったらfalse
を返すので、空の配列だとtrue
になる。some()
は少なくとも1つの要素が条件を満たした場合にtrue
を、それ以外の場合はfalse
を返す。ひとつでも条件を満たすものがあったらtrue
を返すので、空の配列だとfalse
になる。
> [].every(v => v > 0 ) true > [].some(v => v > 0 ) false
reduce()
reduceRight()
reduce()
は左から、reduceRight()
は右から要素を処理して reduce する
> [1,2,3,4,5].reduce((acc,cur) => acc+cur, 0) 15
初期値を省略したら最初の要素が初期値になる。
> [1,2,3,4,5].reduce((acc,cur) => acc+cur) 15
この場合、最初の呼び出しで1番目と2番目の要素が渡されるので、1回処理が減る。
// 初期値を指定する場合 > [1,2,3,4,5].reduce((acc,cur) => {console.log(`(${acc}, ${cur})`); return acc+cur;}, 0) (0, 1) (1, 2) (3, 3) (6, 4) (10, 5) 15 // 初期値を指定しない場合 > [1,2,3,4,5].reduce((acc,cur) => {console.log(`(${acc}, ${cur})`); return acc+cur;}) (1, 2) (3, 3) (6, 4) (10, 5) 15
- reduce で表現された処理はすぐ複雑になって理解するのが難しくなるから、普通のループを使う方が読み書きしやすいかも
flat()
flatMap()
- ES2019 で導入された
flat()
は配列をフラットにできる。引数なしだと1段階。引数でフラット化のレベルを指定できるflatMap()
はmap
の結果をフラット化できる
concat()
- 配列をつなげることができる
- 配列の要素は展開される(再帰的には展開されない)
> let a = [1,2,3] undefined // 複数要素を追加 > a.concat(4,5) [ 1, 2, 3, 4, 5 ] // 配列は展開される > a.concat([4,5],[6,7]) [ 1, 2, 3, 4, 5, 6, 7 ] // 配列の配列は展開されない > a.concat(4,[5,[6,7]]) [ 1, 2, 3, 4, 5, [ 6, 7 ] ] // 元の配列は変更されない > a [ 1, 2, 3 ]
spread operator で良さそうかな。
push()
pop()
shift()
unshift()
push()
pop()
: array を stack のように扱うことができるpush()
には複数の要素を渡すことができるけどconcat()
と違って配列は展開されないunshift()
shift()
: array の末尾ではなく先頭に対して処理を行うpush()
とshift()
を使うと queue のように扱うことができるunshift()
に複数の要素を渡すと前から順番に追加されるのではなくて、一気に追加されることに注意
// 順番に追加した場合 > let a = [] undefined > a.unshift(1) 1 > a.unshift(2) 2 > a [ 2, 1 ] // 一気に追加 > a = [] [] > a.unshift(1,2) 2 > a [ 1, 2 ]
slice()
- 指定された配列のスライスを返す。
- 最初の引数が開始インデックスで、2番目の引数が終了インデックス。終了インデックスは含まれない。
- 開始インデックスだけを渡すと、そこから最後まで
- 負のインデックスを渡すと
length
からの相対的な位置になる。つまり-1
は最後の要素。 - 元の配列は変更されない
splice()
- 配列に要素を追加したり、配列から要素を削除したりする。
slice()
と名前が似てるけど全然違う動きをするから注意。 slice()
やconcat()
と違って元の配列を変更する。- 個人的には引数が分かりにくいし、あんまり使いたくないかなぁ。使うときは調べてから使おっと。
fill()
- 配列を指定した値で埋める
- 元の配列を変更する
- オプショナルな2番目と3番目の引数はそれぞれ開始インデックスと終了インデックス(含まない)。
copyWithin()
- 配列のスライスをその配列の新しい場所にコピーする
- 元の配列を変更する
- これも頭の片隅に覚えておくぐらいにしといて、使うときに調べよっと
indexOf()
lastIndexOf()
- それぞれ前からと後ろからと探してインデックスを返す
- 見つからなかったら
-1
を返す - 比較には
===
を使用する - オブジェクトの場合は参照が一致するかを見るので、内容を見たい場合は
find()
を使って自分で作った predicate を渡せばいい - 開始インデックスを指定することもできる
- 文字列にも
indexOf()
とlastIndexOf()
があって array のメソッドと似てるんだけど、開始インデックスに負の値を指定しても0
として扱われる点に注意
includes()
- ES2016
- 配列が指定された値を含む場合は
true
を、そうでない場合はfalse
を返す - 比較には SameValueZero を使用するため
NaN
をチェックすることができる
sort()
- 元の配列を並び替える
- 何も指定しなかったらアルファベット順に並び替える。
undefined
は最後になる - 比較の関数を指定することができる
reverse()
- 元の配列を逆順に書き換える
join()
toString()
- 文字列に変換するメソッド。ログやエラーメッセージ用に使うことができる
- あとで使うためにシリアライズしたりしたい場合はこれらのメソッドではなく
JSON.stringify()
を使う join()
: 引数でセパレーターを指定して結合できる。引数なしだとカンマでつなぐ。toString()
:join()
の引数なしバージョンと同じ
出力内容には[]
とかはないことに注意
> [1,2,3].toString() '1,2,3' > ["a","b","c"].toString() 'a,b,c' > [1,[2,"c"]].toString() '1,2,c'
Array.isArray()
- static method
- 配列かどうかをチェックする
7.9 Array-Like Objects
length
プロパティがあって、それに対応する非負の整数をプロパティとして持っているオブジェクト- 例えば
document.querySelectorAll()
は array-like オブジェクトを返す - for ループで配列のようにイテレートすることができるし、配列のメソッドを適用したりすることができる
> let a = {"0": "a", "1": "b", "2": "c", length: 3} undefined > Array.prototype.join.call(a) 'a,b,c' > Array.prototype.map.call(a, x => x.toUpperCase()) [ 'A', 'B', 'C' ]
Array.from()
で true array に変換できる
今日はここまでー。次は Functions だなー。先は長い。