Javascriptの配列操作を忘れすぎるのでメモ
配列編
配列を調べる
そいつが配列がどうかを調べる
Array.isArray()
配列ならtrue、そうじゃないならfalseが返る
関数の戻り値が配列だったりそうじゃなかったりする事がままあるので意外によく使う
たとえばファイル選択ダイアログを呼び出した時、戻り値が単一の文字列か、文字列の配列(複数ファイル選択した場合)かを調べなければならない。
code:js
//フォルダ選択ダイアログを表示して取得
const options: dialog.OpenDialogOptions = {
defaultPath: defaultPath,
directory: true,
filters: name: 'Images', extensions: 'jpeg', 'jpg', "png", "gif" },
multiple: false, //複数指定を禁止してはいるけどね
recursive: false,
title: 'Set Move Folder',
}
//フォルダパスを取得
//dialog.openは Promise<string | string[] | null> を戻す
const result = await dialog.open(options)
const resultstr = (Array.isArray(result)) ? result0 : result;
//nullか空文字だった場合初期値をそのまま返す
return resultstr ? resultstr : defaultPath;
配列の長さ(要素数)を調べる
Array.prototype.length
<結果:要素数> = <配列>.length
配列に要素が含まれているか調べる
Array.prototype.includes()
<結果:true/false> = <配列>.includes(<調べたい要素>)
<結果:true/false> = <配列>.includes(<調べたい要素>,<調べ始めるインデックス番号>)
要素が見つかった場合はtrue、見つからなかった場合はfalseが返る。昔は存在確認にもindexOfが使われていたがもう古い。
code:js
const pets = 'cat', 'dog', 'bat';
console.log(pets.includes('cat')); //-> true
要素の位置(添字・インデックス番号)を調べる
Array.prototype.indexOf()
<結果:インデックス番号> = <配列>.indexOf(<調べたい要素>)
<結果:インデックス番号> = <配列>.indexOf(<調べたい要素>,<調べ始めるインデックス番号>)
要素が見つからなかった場合は-1が返る。存在するか調べる目的ではincludesをつかうこと
code:js
const beasts = 'ant', 'bison', 'camel', 'duck', 'bison';
console.log(beasts.indexOf('bison')); //-> 1
console.log(beasts.indexOf('bison', 2)); //-> 4
console.log(beasts.indexOf('giraffe')); //-> -1
要素の追加
最後尾にひとつ(以上)追加
Array.prototype.push()
<新しい配列の長さ> = <配列>.push(<新しい要素>)
<新しい配列の長さ> = <配列>.push(<新しい要素>,<新しい要素>,<新しい要素>...)
配列は追加したぶんだけ増えた状態に更新される(破壊的メソッド)
返す値は「長さ」であることに注意
先頭にひとつ(以上)追加
Array.prototype.unshift()
<新しい配列の長さ> = <配列>.unshift(<新しい要素>)
<新しい配列の長さ> = <配列>.unshift(<新しい要素>,<新しい要素>,<新しい要素>...)
配列は追加したぶんだけ増えた状態に更新される(破壊的メソッド)
返す値は「長さ」であることに注意
unshift()はpush()より遅いことに注意
一度に複数個追加する(=並べた順番で追加する)場合と、複数回unshiftを行う(=毎回全体をずらす)場合では同じ結果にならないことに注意
code:js
let arr = 4, 5, 6;
arr.unshift(1, 2, 3);
console.log(arr); // 1, 2, 3, 4, 5, 6
arr = 4, 5, 6; // 配列をリセット
arr.unshift(1);
arr.unshift(2);
arr.unshift(3);
console.log(arr); // 3, 2, 1, 4, 5, 6
ふたつの配列を合体する
Array.prototype.concat()
<合体後の新しい配列> = <配列A>.concat(<配列B>)
配列Aの後ろに配列Bが追加された新しい配列を作る。
配列は新しく作られるため、配列A/Bに影響を与えない。
code:js
const array1 = 'a', 'b', 'c';
const array2 = 'd', 'e', 'f';
const array3 = array1.concat(array2);
console.log(array3);//-> Array "a", "b", "c", "d", "e", "f"
要素の削除
添字(インデックス番号)がわかっている場合
Array.prototype.splice()
<配列>.splice(<削除範囲開始インデックス番号>, <削除する個数>)
<配列>.splice(<削除する要素のインデックス番号>) ※1個のみ削除する場合
添字がわからない/要素の内容から削除したい場合
Array.prototype.filter()
<配列>.filter(<コールバック関数>)
コールバック関数は各要素を引数とする。trueが返ったらその要素は残る。
本来は「配列の全要素から条件を満たした内容の要素のみ残した配列を作る」メソッド。つまり「指定した内容と一致しない要素」のみで配列を作れば、その要素を削除したことになる
指定した内容と同じ内容の要素は全て削除される
code:js
const words = 'a', 'b', 'c', 'd', 'e', 'd', 'c', 'b', 'a';
const result = words.filter((w) => w != "e"); // w != "e" はwがeじゃないときにtrueが返る式
console.log(result);
//> Array "a", "b", "c", "d", "d", "c", "b", "a" // eが二箇所抜けている
最後尾からひとつだけ削除したい場合
Array.prototype.pop()
<最後尾の要素> = <配列>.pop()
最後尾の要素を取り出す。配列はひとつ減った状態に更新される。
配列が空だった場合undefinedが返る
code:js
const arr = 'a', 'b', 'c', 'd'
console.log(arr.pop()) //-> "d"
console.log(arr) //-> Array "a", "b", "c"
先頭からひとつだけ削除したい場合
Array.prototype.shift()
<先頭の要素> = <配列>.shift()
先頭の要素を取り出す。配列はひとつ減った状態に更新される。
配列が空だった場合undefinedが返る
shift()はpop()より遅いことに注意
先頭からn個が必要で、それ以降を消したい/個数上限を設けたい場合
Array.prototype.length
<配列>.length = <個数>
lengthに値を代入すると配列はその個数に制限され、以降はカットされる
すなわちarr.length = 0でarrは空になる
code:js
const arr = 'a', 'b', 'c', 'd'
arr.length = 3
console.log(arr) //-> Array "a", "b", "c"
余談:ちなみにlengthの数を増やすと増えた要素はundefinedになる
使ってはいけない
delete演算子
指定した要素がundefinedになるだけで、配列の個数は減らない
配列をループで回す
for + カウンタ
code:js
const arr = "a", "b", "c"; //以降この行は省略
for(let i=0; i<arr.length; i++) {
console.log(arri)
}
Cから受け継ぐいにしえのスタイル。タイプ数が多くなるので敬遠されがち
for ... of
for ... of
code:js
for(const item of arr) {
console.log(item)
}
ofで各要素が取れる。シンプルでわかりやすい
取得される要素はインデックス順である(が、インデックス値が重要ならentriesを使ったほうがよかろう)
内容がundefinedでもスキップされたりはしない
for ... in ではないことに注意。
inは要素の「インデックス値」を取る。inで書きたい場合は以下のようになる
code:js
for(const i in arr) {
console.log(arri)
}
取得される要素の順番は必ずしもインデックス順ではない 詳しくは for...in
なにかと不便が多いので配列に対してfor...inを使うことはめったにない
インデックス値と内容を同時に取る (entries())
code:js
for (const index, element of arr.entries()) {
console.log(index, element);
}
通常の配列だとあまりありがたみを感じないかも。連想配列で活躍する。
配列の特殊なループの回し方
すべての要素を計算してひとつの結果を出す(例えば合計)
Array.prototype.reduce()
code:js
const array1 = 1, 2, 3, 4;
// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
(accumulator, currentValue) => accumulator + currentValue,
initialValue,
);
console.log(sumWithInitial); // Expected output: 10
<配列>.reduce((<カウンタ>, <要素>)=><計算内容>, <初期値(省略可)>)
カウンタは通常触らない。名前も何でも良い。
要素は配列から取り出したもの。total = reduce((a, item)=>a+item.value,0)のように要素のキーを参照できる。
配列をもとに新しい配列を作り出す
Array.prototype.map()
code:js
const array1 = 1, 4, 9, 16;
// Pass a function to map
const map1 = array1.map((x) => x * 2);
console.log(map1);
// Expected output: Array 2, 8, 18, 32
配列からいらない要素を消す
Array.prototype.filter()
code:js
const words = "spray", "elite", "exuberant", "destruction", "present";
const result = words.filter((word) => word.length > 6);
console.log(result);
// Expected output: Array "exuberant", "destruction", "present"
Array.filter((要素)=><必要な条件>)。条件に合う要素のみが残る。
ForEachというものもあるがべつのもので代用できることがほとんどなのでほぼ使わない。
配列の変換
内容を全てつなげてひとつの文字列にする
Array.prototype.join()
<結果:文字列> = <配列>.join(<区切り文字。省略した場合はカンマ「,」>)
code:js
const elements = 'Fire', 'Air', 'Water';
console.log(elements.join()); //-> "Fire,Air,Water"
console.log(elements.join('')); //-> "FireAirWater"
console.log(elements.join('-')); //->"Fire-Air-Water"
タグクラウドを作ったりするのによく使う
ちなみにArray.prototype.toString()でもカンマ区切りの文字列になる模様…だが見通しが悪くなるのでおすすめしない
文字列から区切り文字で区切って配列にする
String.prototype.split()
<新しい配列> = <文字列>.split([<区切り文字>[, <要素数上限>]])
区切り文字に空文字を指定した場合、全ての文字が1文字(utf-8単位)ごとに刻まれ配列に入る
区切り文字を指定しなかった場合、文字列がそのまま入った要素数1の配列ができる
code:js
const str = 'The quick brown fox jumps over the lazy dog.';
console.log(str.split(' '))
//> Array "The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog."
ソート(並べ替え)する
Array.prototype.sort()
<ソートされた配列> = <配列>.sort()
<ソートされた配列> = <配列>.sort((a, b) => { <aとbを比較する式> })
返り値をどう使うか関係なく配列はソートされた状態に更新される。(破壊的メソッド)
なにも指定しない場合、要素の内容を文字列に変換してからソートを行う。これは桁の違う数値をソートするときに問題になる
code:js
const ar = 1, 30, 4, 21, 100000
ar.sort()
console.log(ar) //> Array 1, 100000, 21, 30, 4
数値ソートしたい場合は式を定義する必要がある。
> 0 a を b の後に並べる
< 0 a を b の前に並べる
=== 0 a と b の元の順序を維持する
つまり a-bを返せば昇順になる。
code:js
const ar = 1, 30, 4, 21, 100000
ar.sort( (a,b) => a-b )
console.log(ar) //> Array 1, 4, 21, 30, 100000
連想配列(オブジェクト)編
連想配列(オブジェクト)を調べる
そいつが連想配列がどうかを調べる
おそらく…方法は無いのかも? 調査中
typeofでは配列と連想配列の区別はつかない(両方object)、のうえでisArrayで配列の可能性を落とす、くらいしか。
でも配列・文字列・連想配列の区別で困った事はいまのところない。あったら調べて追記する
明確に連想配列である型にMapがある。Mapとして宣言しておけば型チェックの問題はクリアする
が、Mapは発展途上なのか、まだ使いにくい…
連想配列(オブジェクト)の長さ(要素数)を調べる
Object.keys(<連想配列>).length
code:js
const obj = { a: 1, b : 2, c : 3 };
console.log(Object.keys(obj).length) //-> 3
values()の数を数えてもいいと思う
連想配列(オブジェクト)に指定のキーが含まれているか調べる
Object.keys(<連想配列>).includes(<調べたい要素>)
code:js
const car = { make: 'Honda', model: 'Accord', year: 1998 };
console.log(Object.keys(car).includes("make")); //-> true
<調べたい要素> in <連想配列>
code:js
const car = { make: 'Honda', model: 'Accord', year: 1998 };
console.log('make' in car); //-> true
Object.hasOwn(<連想配列>, <調べたい要素>)
code:js
const car = { make: 'Honda', model: 'Accord', year: 1998 };
console.log(Object.hasOwn(car,'make')); //-> true
in演算子やhasOwn()の場合、連想配列のキーの存在の判定というよりは「オブジェクトがそのプロパティを持っているか」の判定という意味合いが強くなる。よりキーをキーとして扱いたい場合はkeys().includes()のほうが自然な気がする
連想配列(オブジェクト)に指定の内容(value)が含まれているか調べる
Object.values(<連想配列>).includes(<調べたい要素>)
code:js
const obj = { a: 1, b : 2, c : 3 };
console.log(Object.values(obj).includes(2)) //-> true
要素の追加
obj["<キー>"] = <値>
obj.<キー> = <値>
キーが存在しなければ新しいキーが追加される。存在すれば値で更新される。
要素の削除
キーがわかっている場合
delete 演算子
delete obj["<キー>"]
delete obj.<キー>
code:js
const Employee = { firstname: 'John', lastname: 'Doe'};
console.log(Employee.firstname) //->"John"
delete Employee.firstname;
console.log(Employee.firstname) //->undefined
delete Employee"lastname";
console.log(Employee) //->{}
キーがわからない/要素の内容から削除したい場合
キーに対してfor ... ofで内容をサーチする
code:js
const obj = { "a": 1, "b": 2, "c":3 }
for(const key of Object.keys(obj)) {
if(objkey==2) {
delete objkey
}
}
console.log(obj) //-> Object { a: 1, c: 3 }
もっと短く
code:js
const obj = { "a": 1, "b": 2, "c":3 }
Object.keys(obj).forEach((k)=>{if(objk==2) delete objk})
console.log(obj) //-> Object { a: 1, c: 3 }
内容をサーチするだけならObject.values()で取れるが、その後deleteするにはキーを知らないといけない。
連想配列をループで回す
キーのリストを取得してfor ... ofで回す
code:js
const obj = { a: 1, b: 2, c: 3 };
for (const key of Object.keys(obj)) {
console.log(${key}: ${obj[key]});
}
/*
"a: 1"
"b: 2"
"c: 3"
*/
for ... in はおすすめしない。
for...in 文は、キーが文字列であるオブジェクトの列挙可能プロパティすべてに対して、継承された列挙可能プロパティも含めて反復処理を行います
「列挙可能」プロパティってなんだ?って人は for ... inを使わないほうがいいと思う
連想配列というよりもオブジェクトのプロパティを調べる目的で使うときがくるかもしれない
キーと内容を同時に取る (entries())
code:js
const obj = { "a": 1, "b": 2, "c":3 }
for (const key, value of Object.entries(obj)) {
console.log(${key}: ${value});
}
便利。
間違えやすいやつ
基本的にオブジェクト自身が規定のメソッドを持つことはない、と考える。つまり
❌️someObj.keys() → ⭕ Object.keys(someObj)
❌️someObj.entries() → ⭕ Object.entries(someObj)
❌️someObj.hasown(key) → ⭕️ Object.hasown(someObj, key)
配列がsomeArray.length()など規定のメソッドを持つ点とは対照的。