Arrayオブジェクトについて
Arrayオブジェクトとは
値の集合を管理と操作をする。集合には3種類の特性を持つオブジェクトが存在する。
Array
- インデックスで管理
- 値の重複OK
let list = ["tanaka","suzuki","yamada"];
/* 指定要素で配列生成 */
let list = new Array("tanaka","suzuki","yamada");
/* 空配列生成 */
let list = new Array();
/* 指定サイズで空配列生成 */
let list = new Array(5);
要素の追加と削除。
let list = ["tanaka","suzuki","yamada"];
/* 先頭に追加 */
list.unshift("okumura");
/* 末尾に追加 */
list.push("ooyama");
console.log(list);//['tanaka', 'suzuki', 'yamada']
/* 複数の要素を追加 */
list.push("maeda","yamamoto");
list.push(...["enosaka","wanaka"])
list.concat(["okuura","touyama"],["urasaka","nakamura"]);
console.log(list);// ['okumura', 'tanaka', 'suzuki', 'yamada', 'ooyama', 'maeda', 'yamamoto', 'enosaka', 'wanaka']
/* 先頭を取得&削除 */
console.log(list.shift());//okumura
console.log(list);//['tanaka', 'suzuki', 'yamada', 'ooyama', 'maeda', 'yamamoto', 'enosaka', 'wanaka']
/* 末尾を取得&削除*/
console.log(list.pop());//wanaka
console.log(list);// ['tanaka', 'suzuki', 'yamada', 'ooyama', 'maeda', 'yamamoto', 'enosaka']
複数要素の追加/置換/削除。
-
splice(start [,count [items ,...]]):配列の任意の個所に要素を追加したり、既存の要素を置き換え削除したりできる。
let list = ["い","ろ","は","に","ほ","へ","と"]; /* 置換 */ list.splice(3, 2, "x", "y", "z"); console.log(list);//['い', 'ろ', 'は', 'x', 'y', 'z', 'へ', 'と'] /* 挿入 */ list.splice(3, 0, "x", "y", "z"); console.log(list);//['い', 'ろ', 'は', 'x', 'y', 'z', 'x', 'y', 'z', 'へ', 'と'] /* 削除 */ list.splice(3, 2); console.log(list);//['い', 'ろ', 'は', 'z', 'x', 'y', 'z', 'へ', 'と'] list.splice(3); console.log(list);//['い', 'ろ', 'は'] -
slice([start [, end]]):特定の範囲から要素を取得。
let list = ["い","ろ","は","に","ほ","へ","と"]; console.log(list.slice(3, 6));//['に', 'ほ', 'へ'] console.log(list.slice(3));//['に', 'ほ', 'へ', 'と'] console.log(list.slice());//['い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と'] console.log(list.slice(-3, 6));//['ほ', 'へ'] console.log(list.slice(3, -1));//['に', 'ほ', 'へ'] console.log(list.slice(7, 10));//[] console.log(list.slice(3, 1));//[] -
indexOf(searchElement [, fromIndex]):配列の要素を検索
-
lastIndexOf(searchElement [, fromIndex]):配列の要素を検索
let list = [10, 20, 30, 20, 50]; console.log(list.indexOf(20));//1 console.log(list.indexOf(60));//-1 console.log(list.lastIndexOf(20));//3 console.log(list.indexOf("20"));//-1 console.log(list.indexOf(20, 2));//3 console.log(list.lastIndexOf(20, 2));//1 -
include(searchElement [,fromIndex]):登場位置に関係なく要素を含んでいるかの確認。
let list = [10, 20, 30, 20, 50]; console.log(list.includes(10));//true console.log(list.includes(60));//false console.log(list.includes("10"));//false console.log(list.includes(10, 1));//false console.log(list.includes(20, 2));//true -
let list = ['ド', ['レ', 'ミ', ['ファ', 'ソ', ['ラ', 'シ']]]]; console.log(list.flat());//['ド', 'レ', 'ミ', Array(3)] console.log(list.flat(2));//['ド', 'レ', 'ミ', 'ファ', 'ソ', Array(2)] console.log(list.flat(Infinity));//['ド', 'レ', 'ミ', 'ファ', 'ソ', 'ラ', 'シ'] -
join([separator]):配列要素を指定の区切り文字で連結し、文字列化。
let list = ["上","左","右"]; console.log(list.join());//上,左,右 console.log(list.join("/"));//上/左/右 console.log(list.join("\\t"));//上 左 右 console.log(list.join(""));//上左右 -
copyWithin(target [,start [, end]]):配列内の要素を移動。
let list1 = ["和歌山","大阪","京都","",""]; console.log(list1.copyWithin(2, 0, 3));//['和歌山', '大阪', '和歌山', '大阪', '京都'] -
Array.from(obj [,mapfn [,thisArg]]):配列ライクなオブジェクトを配列に変換。例:map,set,HTMLCollection,argument,Stringなど
let opts = Array.from(document.querySelector('#id').options); opts.forEach(function(opt) { console.log(opt.value); });
配列の要素を並べ替える。
- sort():要素を並び替える。
- reverse():要素を逆順に並び替える。
let list = ["い","ろ","は","に","ほ","へ","と"];
console.log(list.sort());//['い', 'と', 'に', 'は', 'へ', 'ほ', 'ろ']
console.log(list.reverse());//['ろ', 'ほ', 'へ', 'は', 'に', 'と', 'い']
数値の場合は意図したように動かないことがある。なので、コールバック関数を指定して並べ替えのルールを変更してから数値の並べをする。
let list = [5, 25, 10];
console.log(list.sort());//[10, 25, 5]
console.log(list.sort(function(m, n) { //[5, 10, 25]
return m - n;
}))
コールバック関数を使うことができれば並び替えのルールを好きに変更することができる。
let classes = [ '部長', '課長', '主任', '担当' ];
let members = [
{ name: '鈴木清子', clazz: '主任' },
{ name: '山口久雄', clazz: '部長' },
{ name: '井上太郎', clazz: '担当' },
{ name: '和田知美', clazz: '課長' },
{ name: '小森雄太', clazz: '担当' },
];
console.log(members.sort(function(x, y) {
return classes.indexOf(x.clazz) - classes.indexOf(y.clazz);
}));
/*
{name: '山口久雄', clazz: '部長'}
{name: '和田知美', clazz: '課長'}
{name: '鈴木清子', clazz: '主任'}
{name: '井上太郎', clazz: '担当'}
{name: '小森雄太', clazz: '担当'}
*/
配列を指定されたルールで加工する。
mapメソッド:配列を指定された関数で加工する。返り値として加工した結果をかえす。
list.map(function(value, index, array){
...statement...
}, thisArg);
let list = [1, 2, 3, 4, 5];
list.map(function(value, index, array) {// [1, 4, 9, 16, 25]
return value * value;
});
任意の条件式によって配列を検索する。
findメソッド:要素の値を等価検索することができ、より複雑な検索条件を設定できる。配列の内容をコールバック関数で判定し、最初に合致した(戻り値がtrue)要素を取得する。
list.find(function(value, index, array){
...statement...
});
let books = [
{title:"おじいちゃんと一緒",price:1000},
{title:"デザインと料理",price:2200},
{title:"車2000GTを解剖",price:5000},
];
console.log(books.find((value, index, array) => {//{title: '車2000GTを解剖', price: 5000}
return value.title.startsWith("車");
}));
条件式に合致する要素が存在するかを判定する。
someメソッド:条件式に合致する要素が一つでも存在する場合にtrueを返す。
list.some(function(value, index, array){
...statement...
});
everyメソッド:条件式に合致する要素が全て存在する場合にtrueを返す。
list.every(function(value, index, array){
...statement...
});
let books = [
{title:"javascript入門", price:1200},
{title:"python初心者", price:200},
{title:"html入門", price:4000},
{title:"cssでデザイン", price:10000},
{title:"docker環境", price:43},
];
console.log(books.some(function (value) {//true
return value.price < 3000;
}));
console.log(books.every(function(value){//false
return value.price < 3000;
}));
制御構文を使ってループ処理を中断する。
let list = [10, 42, null, 31, 68];
list.some(function(value){
if(value === null) return true;//10 42 true
console.log(value);
});
配列から条件に合った要素だけ取得
filterメソッド:条件似合った要素を取得。配列の内容をコールバック関数で判定し、trueを返した要素だけでを残す。falseは除外。
list.fillter(function(value, index, array){
...statement...
}, this.Arg);
let books = [
{title:"javascript入門", price:1200},
{title:"python初心者", price:200},
{title:"html入門", price:4000},
{title:"cssでデザイン", price:10000},
{title:"docker環境", price:43},
];
console.log(books.filter(function(value){
return value.price < 3000;
}));
/*
0:{title: 'javascript入門', price: 1200}
1:{title: 'python初心者', price: 200}
2:{title: 'docker環境', price: 43}
length:3
*/
配列内の要素を順に処理して1つにまとめる
reduceメソッド:配列の要素を順に処理してまとめる。コールバック関数に仮変数resultを記述することで直前の結果を返すことができる。
list.reduce(function(value, index, array){
...statement...
}, initial);
let list = [4, 2, 6, 1];
console.log(list.reduce(function(result, value){//48
return result * value
}));
console.log(list.reduce(function(result, value){//96
return result * value
}, 2));
resultには先頭の要素の値が渡される。引数initialに引数resultの初期値を渡すこともできる。
配列の右から左方向に演算する
reduceRightメソッド:reduceメソッドとは逆方向から順に処理してまとめる。
let list = [
["solo", 1], ["duo", 2], ["trio",1],
];
console.log(list.reduce(function(result, value){//['solo', 1, 'duo', 2, 'trio', 1]
return result.concat(value);
}));
console.log(list.reduceRight(function(result, value){//['trio', 1, 'duo', 2, 'solo', 1]
return result.concat(value);
}));
シャローコピー(浅いコピー)とは
オブジェクトの参照だけが複製されること。 以下の例であれば、list2の値を変更した時にcopy2の中身まで変更されてしまっている。
let list1 = [1, 2, 3, 4, 5];
let list2 = [
[10, 20, 30],
[40, 50, 60],
[80, 80, 90],
];
let copy1 = Array.from(list1);
let copy2 = Array.from(list2);
list1[0] = 999;
list2[0][0] = 777;
console.log(list1);//[999, 2, 3, 4, 5]
console.log(copy1);//[1, 2, 3, 4, 5]
console.log(list2[0]);//[777, 20, 30]
console.log(copy2[0]);//[777, 20, 30]
シャローコピーの問題を回避するためにはいくつかの方法がある。
- スプレッド構文
- sliceメソッド
- concatメソッド
Mapオブジェクト
keyとvalueのセット(連想配列)を管理する為のオブジェクト。
- keyと値で管理
- keyの重複はNG
Mapオブジェクトを使用するときは、Mapコンストラクタを使用する。
new Map([[key, value], ...]);
引数には2次元配列を渡していく。
let map = new Map([
["tea","お茶"],
["coffee","コーヒー"],
["water","水"],
]);
console.log(map);//{'tea' => 'お茶', 'coffee' => 'コーヒー', 'water' => '水'}
keyとvalueの配列を渡せば良いだけなので、keyの配列とvalueの配列を用いて表現可能。
let keys = [1, 2, 3, 4];
let values = ["a", "b", "c", "d"];
let map = new Map(
keys.map(function(value, index, array){
return [value, values[index]];
}));
console.log(map);//{1 => 'a', 2 => 'b', 3 => 'c', 4 => 'd'}
オブジェクトリテラルとの相違点
-
任意の型でkeyを設定できる。
オブジェクトリテラルは文字列しか設定できないが、Mapオブジェクトでは任意の型を設定できる。(NaN、undefinedも可能)
-
mapのサイズを取得できる。
sizeプロパティでmapのサイズを取得できる。
let map = new Map([ ["tea","お茶"], ["coffee","コーヒー"], ["water","水"], ]); console.log(map.size);//3 -
クリーンなマップを作成できる。
オブジェクトリテラルの実態はObjectオブジェクト(オブジェクトのPrototype)。配下には標準で用意されているプロパティ(キー)が存在する。オブジェクトリテラルを作成した時に厳密に言えば、すでに空ではない。 Mapオブジェクトは完全に空の連想配列を生成できる。 詳細はMDN公式参照。
-
パフォーマンスに優れる。
- Mapは反復可能なので直接反復処理を行うことができるが、Objectは反復処理を実装していない為、直接反復処理を行うにはキーの取得からしなくてはいけない。
- Mapは値が挿入された順に処理を行う為、挿入順を考慮しなくて済む。Objectは挿入順を保持しない為、順番を考慮しなくてはいけない。
マップの値を設定/取得する
setメソッド:マップの値を設定する。
dic.set(key, value);
getメソッド:マップの値を取得する。
dic.get(key);
let dic = new Map();
dic.set(1, "a")
.set(2, "b")
.set(3, "c")
.set(1, "a");
console.log(dic);//{1 => 'a', 2 => 'b', 3 => 'c'}
console.log(dic.get(1));//a
console.log(dic.get(2));//b
console.log(dic.get(4));//undefined
メソッドの後にドット演算子をつなげてメソッドを呼び出すことができる。これはメソッドチェーンと呼ばれている呼び出し方法。
keyを扱う際の注意点
keyに任意の型を設定できることから、間違えやすいポイントがある。
-
キーは厳密等価で比較すること。
let dic = new Map(); dic.set("1", "a"); console.log(dic.get(1));//undefined -
参照型のキーは参照値が比較される。
let dic = new Map(); dic.set(["a", "b", "c"], "a"); dic.get(["a", "b", "c"]);//undefined -
NaN === NaNはtrue。
let dic = new Map(); dic.set(NaN, "not a number"); dic.get(NaN);//not a number
マップから既存のキーを削除する
deleteメソッド:キーを指定してマップから削除する。
let dic = new Map();
dic.set("1st",1);
dic.set("2nd",2);
console.log(dic.delete("1st"));//true
console.log(dic.delete("3rd"));//false
console.log(dic);//{'2nd' => 2}
マップから全てのキー/値を取得する
keysメソッド:全てのキーを取得。
valuesメソッド:全ての値を取得。
entriesメソッド:全てのキーと値を取得。
let dic = new Map();
dic.set(1, "a")
.set(2, "b")
.set(3, "c")
.set(1, "a");
console.log(dic.keys());//{1,2,3}
console.log(dic.values());//{'a', 'b', 'c'}
console.log(dic.entries());//{1 => 'a', 2 => 'b', 3 => 'c'}
for(let [k, v] of dic.entries()) {
console.log(`k:${k} v:${v}`);
}
/*
k:1 v:a
k:2 v:b
k:3 v:c
*/
マップの内容を順に処理する
forEachメソッド:マップの内容を順に直接処理する。
dic.forEach(function(value, key, map){
...statements...
}, thisArg);
let dic = new Map();
dic.set(1,"a")
.set(2,"b")
.set(3,"c");
dic.forEach(function(value, key){
console.log(`value:${value} key:${key}`);
});
/*
value:a key:1
value:b key:2
value:c key:3
*/
ObjectとMapを相互変換する
Object.fromEntriesメソッド:MapをObjectに変換する。
let dic = new Map();
dic.set(1,"a")
.set(2,"b")
.set(3,"c");
console.log(Object.fromEntries(dic));
/*
{1: 'a', 2: 'b', 3: 'c'} [[Prototype]]: Object
*/
Object.entriesメソッド:ObjectをMapに変換する。
let obj = {title:"javascript講座"};
console.log(new Map(Object.entries(obj)));//Map(1) {'title' => 'javascript講座'}
WeakMapオブジェクト
WeakMapはキーはオブジェクトのみで、値は任意の値にできるコレクション。キーによるオブジェクトへの参照は弱く保持され(弱参照)、オブジェクトへの参照が他に存在しないときはガベージコレクションの対象となる。その為、メモリリークを解消することができる。 WeakMapオブジェクトはMapオブジェクトとは異なり、列挙不可能であること。このことから、キーのリストを取得するメソッドなどをもたない。
let obj = {};
let weakMap = new WeakMap();
weakMap.set(obj, "a");
console.log(weakMap);
/*
WeakMap {{…} => 'a'}
[[Entries]]
0:{Object => "a"}
[[Prototype]]:WeakMap
constructor:ƒ WeakMap()
delete:ƒ delete()
get:ƒ ()
has:ƒ has()
set:ƒ ()
Symbol(Symbol.toStringTag):"WeakMap"
[[Prototype]]:Object
*/
weakMap = null;//GCの時に破棄対象
Set
配列と同じで値を束ねる為のオブジェクト。以下の点が配列と違う。
- 値の順番はない
- 値の重複NG
インデックス番号やキーでアクセスすることができないので、以下の方法で操作を行う。
- hasメソッドで値の有無を判定。
- for…of、valuesプロパティで中身を列挙。
セットの初期化にはSetコンストラクタを使う。
new Set([iterable])
iterableとは反復可能なオブジェクトを指す。配列やマップ、Stringなどが反復可能オブジェクト。
let data = new Set([2, 20 ,19, 34, 2]);
console.log(data);//Set(4) {2, 20, 19, 34}
セットでも参照型やNaNの比較ルールは同じ。
セットの追加/削除
addメソッド:値の追加
deleteメソッド:値の削除
clearメソッド:全ての値を削除
set.add(value);
set.delete(value);
set.clear();
let data = new Set([2, 20 ,19, 34, 2]);
console.log(data.add("a"));//{2, 20, 19, 34, 'a'}
console.log(data.delete(2));//true
console.log(data.clear());//undefined
console.log(data);//Set(0) {size: 0}
セットにある値の有無を確認
hasメソッド:指定された値が存在すればtrue、なければfalseを返す。
let data = new Set([2, 20 ,19, 34, 2]);
console.log(data.has(2));//true
console.log(data.has(9));//false
全ての値を取得
forEachメソッドを使った場合、keyとvalueには同じ値が入る。
let data = new Set([2, 20 ,19, 34, 2]);
data.forEach(function(value, key, set){
return console.log(`value:${value} key:${key} set:${set}`);
});
/*
value:2 key:2 set:[object Set]
value:20 key:20 set:[object Set]
value:19 key:19 set:[object Set]
value:34 key:34 set:[object Set]
*/
メソッドチェーン
あるオブジェクトに対してドット演算子を使ってメソッドを繋げていくこと。前のメソッドの結果を受け取って次のメソッドが処理を開始する。
let str = "1234567890";
match = str.replace("456", "abc")
.match("abc");
console.log(match);//['abc', index: 3, input: '123abc7890', groups: undefined]
記述量が少なくなる分の作業効率化が図れるが、繋げすぎるとバグの原因になるので上手に使い分ける必要がある。
値渡しと参照渡し
//値渡し
var a, b;
a = 0;//aが0
b = a;//bが0
b = 5;//bが5
console.log(b); // 5
console.log(a); // 0 (aは変わらない)
//参照渡し
var c, d;
c = [0,1];//cが[0, 1]
d = c;//dが[0, 1]
d[0] = 5;//dの先頭が5 [5, 1]
console.log(d) // [5,1]
console.log(c) // [5,1] (cも変わる)