スタートページ >
Javascript >
配列
配列操作記述の簡素化 Arrayメソッド、アロー関数
私が記述して正しい結果が得られたものだけに限定されています。
[テスト]は、説明と同じ内容を実験したものです。
そのコードは[テストソース]で別ウインドウに表示されます。
主なメソッド一覧
アロー関数表示
配列の追加・削除
pop 末尾要素の削除
push 末尾要素の追加
shift 先頭要素の追加
unshift 先頭要素の削除
concat 配列の結合
Array.Peototype系の代表的メソッド
map 配列の個々の要素を処理する
forEach 配列の個々の要素を処理する
filter 条件に一致する要素を検索し新しい配列を作る
reduce 隣接する要素の加工
sort ソート
配列の要素検索
find 条件を満足する最初の要素の値を戻す
indexOf 一致する要素の位置
every すべての要素が条件をみたすか
some 条件をみたす要素が一つでもあるか
配列の抽出・加工
slice 配列の部分抽出
splice 配列の部分抽出。抽出した部分は元配列から削除
join 配列の全要素を連結した文字列を返す
split 区切文字で配列にする
Obj配列の一覧
keys Obj配列のkey一覧
values Obj配列のvalue一覧
entries key/value のペアの一覧表
アロー関数
アロー関数とは、plus = (a,b) => a+b; のように => を用いた関数式の表示方法です。
アロー関数を用いることにより、プログラムの記述が簡素になります。
通常の表示方法
function 関数名(引数) {
処理結果 = 処理;
return 処理結果;
}
アロー関数の表示方法(1)
ここでは、JavaScript に標準装備されている Array,Prototype系のメソッドを多く取り扱っています。
そのときは、次の形式になります(メソッドとは関数のようなものです)。
処理結果 = 元配列.メソッド名(引数=> 処理)
・引数がないとき
元配列,メソッド名();
・引数が1つのとき
元配列,メソッド名(a => a*a);
・引数が複数のとき
元配列,メソッド名((a,b) => a+b));
・処理が数行になる({ }で囲む)とき
元配列,メソッド名((a,b) => { 処理1; 処理2; return 結果 }; // return が必要
アロー関数の表示方法(2)
一般的には関数変数を次のように定義します。
変数名 = (引数) => { 処理 };
・引数がないとき
var 変数名 = () => alert("こんにちは");
・引数が1つのとき
var 変数名 = a => a*a;
・引数が複数のとき
var 変数名 = (a,b) => a+b;
・処理が数行になる({ }で囲む)とき
var 変数名 = a => {
if (a < 0) a = -a;
return a; // return が必要
};
pop ,push ,
shift ,unshift
pop() 末尾を削除
push() 末尾に追加
shift() 先頭を削除
unshift() 先頭に追加
1次元配列(ベクトル v)対象
元配列 v=[10,20,30,40,50]; // v.length=5
v.pop() 末尾を削除 // v=[10,20,30,40]; v.length=4
v.shift() 先頭を削除 // v=[20,30,40,50]; v.length=4
要素 = 60; // ↓
v.push(要素) 末尾に追加 // v=[10,20,30,40,50,60] v.length=6
要素 = 1; // ↓
v.unshift(要素) 先頭に追加 // v=[1,10,20,30,40,50] v.length=6
テスト
テストソース表示
2次元配列(行列 m)対象
元配列 m=[ ["阿部","文学部",20],
["井上","工学部",20]
]; // m.length=2
追加行ベクトル = ["宇野","文学部",21];
m.pop() 末尾を削除 // m=[ [阿部,文学部,20] ] m.length=1
m.shift() 先頭を削除 // m=[ [井上,工学部,20] ] m.length=1
m.push(行ベクトル) 末尾に追加 // m=[ [阿部,文学部,20].
// [井上,工学部,20],
// [宇野,文学部,21] ]; ←追加 m.length=3
m.unshift(行ベクトル) 先頭に追加 // m=[ [宇野,文学部,21], ←追加
// [阿部,文学部,20],
// [井上,工学部,20] ]; m.length=3
テスト
テストソース表示
concat 配列を結合する、配列のコピー
2次元解列の shift,unshift とほぼ同じ機能です。
元配列 m=[ ["阿部","文学部",20],
["井上","工学部",20]
]; // m.length=2
追加行ベクトル = ["宇野","文学部",21];
m1 = m.concat([v]); ベクトルを末尾に追加 // m1=[ [阿部,文学部,20],
// ↑2次元配列にするには [] で囲む // [井上,工学部,20],
// [宇野,文学部,21] ]; ←追加
m1 = [v].concat(m); ベクトルを先頭に追加 // m1=[ [宇野,文学部,21], ←追加
↑2次元配列にするには [] で囲む // [阿部,文学部,20],
// [井上,工学部,20] ];
この結果、m は変化しません
★配列のコピー
var m1 = m;
このとき、m1 に m の各要素の値がコピーされるのではなく、アドレスが渡されるだけです。
そのため、m1[0][0] = "宇野"; とすると、m[0][0] も "宇野" になり、
また、m[0][1] = "理学部"; とすると、m1[0][1] も "理学部" になります。
これを防ぐには for ループで m1[i][j] = m[i][j]; とすれば、m と m1 は独立します。
concat によるコピーでは、m と m2 は独立します。
var m2 = [];
for (var i=0; i<m.length; i++) {
m2[i] = m[i].concat();
}
テスト
テストソース表示
ArrayPrototypeメソッド
ArrayPrototypeメソッドは、基本的には通常の配列処理の記述を簡素に記述できる機能です。
一般形
新配列 = 元配列.メソッド(function(value,index,array){
配列全体に関する処理:
return 新配列の値;
});
例:元配列 v = [10, 20, 30, 40, 50];
新配列 v1 = v.map(function(value,index,array){ // value だけを使うのでindex,arrayは省略できる
return value*value;
]);
結果 v1 = [100, 400, 900, 1600, 2500]:
アロー関数表示
vi = v.map(value => value**value);
・メソッドには、map. forEach, indexOf など多様なものがあります。f
・引数には次の3つがあります。
第1引数 value(要素の値)
第2引数 index(要素のインデックス番号)
第3引数 array(処理している配列)
・value などの名称は予約語ではなく自由ですが、通常はこれらを用います。
・第1引数は必須ですが、第2・3引数はそれを用いないときは省略できます。
上の場合、v1 = v.map(function(value){ return value*value;}); としても同じです。
・この { } の内部では、配列は Map配列(連想配列)になります。
v1 = [{0.10}. {1,20}, ... {4,50}
それで、 if (index < 2) ~ のような操作もできます。
しかし、{ } を出た後は、v1 は通常配列ですので、このような使い方はできません。
・第3引数 array については、ここでは取り扱いません。
map 配列の全体要素を同一処理
●一次元配列の場合
元配列 v = [10, 20, 30, 40, 50];
単純コピー
var v1 = v.map(function(value,index,array){ // index,array は省略可能
return value; // v1 = [10, 20, 30, 40, 50];
});
コピー後更新 v[0] = 100; v1[1] = 200; // 一方の変更は他に影響しない
// v = [100, 20, 30, 40, 50];
// v1 = [10, 200, 30, 40, 50];
全要素の同一加工
var v1 = v.map(function(value){ // index.array は省略可能
return value*value; // v1 = [100, 400, 900, 1600, 2550];
}); // 全ての要素に適用
次の記述もできる
var v1 = v.map(function(value){
var s = value * value;
return s;
});
アロー関数による表現 v1 = v.map(value => value*value); // v1 = [100, 400, 900, 1600, 2550];
index(発生順)による選択
var v1 = v.map(function(value,index){
var s = value;
if (index < 2) s = value*value; // v1=[100,400,30,40,50]
return s;
});
●二次元配列の場合
配列 m = [
["阿部", "文学部", 20],
["井上", "工学部", 20],
["宇野", "文学部", 21]
];
氏名一覧
v = m.map(function (学生) {
return 学生[0] // 氏名だけを戻す
});
v = m.map(学生 => 学生.氏名);
v = [阿部,井上,宇野]
年齢+1
m1 = m.map(function (学生) {
学生[2] += 1; // 全学生の年齢+1
return 学生 // 行全体を戻す
} );
m1 = m.map(学生 => { 学生[2] += 1; return 学生 } );
m1[0]=[阿部,文学部,21]
m1[1]=[井上,工学部,21]
m1[2]=[宇野,文学部,22]
// (注)「学生」は第1引数(value)で各行の [氏名, 学部, 年齢] が指され、氏名は 学生[0] になる
// 名称は自由で定義不要
●二次元Obj配列の場合
配列 m = [
{氏名:"阿部", 学部:"文学部", 年齢:20},
{氏名:"井上", 学部:"工学部", 年齢:20},
{氏名:"宇野", 学部:"文学部", 年齢:21}
];
氏名一覧
v = m.map(function (学生) {
return 学生.氏名 // 氏名だけを戻す
});
v = m.map(学生 => 学生.氏名);
v = [阿部,井上,宇野]
年齢+1
m1 = m.map(function (学生) {
学生.年齢 += 1; // 全学生の年齢+1
return 学生 // 行全体を戻す
} );
m1 = m.map(学生 => { 学生.年齢 += 1; return 学生 } );
m1[0]={阿部,文学部,21}
m1[1]={井上,工学部,21}
m1[2]={宇野,文学部,22}
テスト
テストソース表示
forEach 配列の個々の要素を順番に処理する
格納されている複数の要素を取り出し、取り出した要素に対して順番に全ての要素に処理を行ないます。
forEach()の返り値はありません。すなわち、 v1 = v.v.forEach(...) のような記述はできません。
そのため。forEach() の内部で処理が完結し、外部と遮断されます。
v = [10, 20, 30, 40, 50];
v.forEach(function(value){
経過 += " value=" + (value + 100); // 各要素に100を加える
document.getElementById(表示場所).innerHTML = 経過;
});
を行うと、value=110, value=120, ..., value=150 となりますが、配列 v は変化しません。
外部との連携をするには、forEach の外部で宣言する必要があります。
全部の合計
var sum = 0;
v.forEach(function(value){
sum += value; // sum = 150
});
v.forEach(value => sum += value);
先頭の3個の合計
var sum = 0;
var item = 3;
v.forEach(function(value,index){
if (index < item) sum += value; // sum = 60 (10+20+30)
});
●2次元配列の場合
m = [
["阿部", "文学部", 20],
["井上", "工学部", 20],
["宇野", "文学部", 21]
];
年齢の合計
var sum = 0;
m.forEach(function(学生){
sum += 学生[2]; // sum = 61
});
m.forEach(学生 => sum += 学生[2]);
文学部の学生数
var cnt = 0;
m.forEach(function(学生){
if (学生[1] == "文学部") cnt++; // cnt=2
});
m.forEach(学生 => {if (学生[1] == "文学部") cnt++;}); // if文がありときは{ }で囲む
テスト
テストソース表示
filter 条件に一致する要素を検索し、合致した新しい配列を作る
配列 v = [10, 20, 30, 40, 50];
値 value < 25 の要素を選択
v1 = v.filter(function(value){
return value <= 25; // v1 = [10,20] v は不変
});
v1 = v.filter(value => value < 25);
発生順序 index >= 2 の要素を選択
v1 = v.filter(function(value,index){
return index >= 2; // v1 = [30,40,50]
});
v1 = v.filter((value,index) => index >= 2)
●2次元配列の場合 m1 = m.filter(学生 => 学生[1] === '文学部')
m = [
["阿部", "文学部", 20],
["井上", "工学部", 20],
["宇野", "文学部", 21]
];
文学部 だけを選択
m1 = m.filter(学生 => 学生[1] === '文学部');
// m1[0]=阿部,文学部,20
// m1[1]=宇野,文学部,21
●2次元Obj配列の場合
m = [
{氏名:"阿部", 学部:"文学部", 年齢:20},
{氏名:"井上", 学部:"工学部", 年齢:20},
{氏名:"宇野", 学部:"文学部", 年齢:21}
];
学部=文学部 だけを選択
m1 = m.filter(学生 => 学生.学部 === '文学部');
// m1 = [
// {氏名:"阿部", 学部:"文学部", 年齢:20},
// {氏名:"宇野", 学部:"文学部", 年齢:21}
// ];
// ここでの「学生」は「各行」の意味。名称は自由だし定義も不要
テスト
テストソース表示
reduce 隣接する要素の加工
配列 v = [30, 20, 50, 10, 40];
合計の計算
var s = v.reduce(function(a, b){
return a + b; │ ─┐
}); │ │
アロー関数 s = v.reduce((a, b) => a + b);
└ 引き数が複数なので () で囲む
処理の動き
reduce(a,b)では、第1引数 a には「蓄積された値」、第2引数 b には「現在の要素」が渡されます。
a には初期値として配列の先頭の要素 30, b にはその次の要素 20 が渡されます。
a b a+b
30 20 50
50 50 100
100 10 110
110 40 150 ←戻り値
最大値の計算
var s = v.reduce(function(a, b){
return Math.max(a,b);
});
アロー関数 s = v.reduce((a, b) => Math.max(a,b));
処理の動き
a b max
30 20 30
30 50 50
50 10 50
50 40 50 ←戻り値
テスト
テストソース表示
sort ソート
私には、内部処理を説明できないのですが、次のコードでソートできます。
配列 v = [30, 20, 50, 10, 40];
昇順ソート → v1 = [10, 20, 30, 40, 50]
v1 = v.sort();
v1 = v.sort(function(a,b){ return a - b;});
v1 = v.sort((a, b) => a - b);
降順ソート → v1 = [50, 40, 30, 20, 10]
v1 = v.sort(function(a,b){ return b - a;});
v1 = v.sort((a, b) => b - a);
●二次元Obj配列のソート
var m = [
{氏名:"阿部", 学部:"文学部", 年齢:20},
{氏名:"井上", 学部:"工学部", 年齢:19},
{氏名:"宇野", 学部:"文学部", 年齢:21}
];
年齢の昇順にソート
m.sort((a,b) => a.年齢 - b.年齢);
ソート後
m[0] 氏名 =井上, 学部=工学部, 年齢=19
m[1] 氏名 =阿部, 学部=文学部, 年齢=20
m[2] 氏名 =宇野, 学部=文学部, 年齢=21
テスト
テストソース表示
find 条件を満足する最初の要素の「値」
配列 v = [10, 20, 40, 40, 50];
要素 >30 の最初の要素
var s = v.find(function(value){
return value > 30; // s = 40
});
アロー関数表示
s = v.find(value => value > 30); // s = 40
要素 <30 の最初の要素
var s = v.find(function(value){
return value < 0; // s = undefined 存在しない
});
テスト
テストソース表示
indexOf / lastindexOf 条件を満足する最初の要素の「位置」
indexOf 先頭から探す indexOf 末尾から探す
配列 v = [10, 20, 40, 40, 50];
要素 40 の最初の位置 i = v.indexOf(40); // i=2
要素 40 の最後の位置 i = v.lastIndexOf(40); // i=3
要素 25 の最初の位置 i = v.indexOf(40); // i=-1 存在しないと -1 が戻る
(二次元配列で適用できれば「阿部がある行番号は?」などができるのですが、うまくいきません)
テスト
テストソース表示
every すべての要素が条件を満たすか
●1次元配列のとき
配列 v = [10, 20, 40, 40, 50];
すべての要素は0より大か? v.every(value => value > 0); // i=true
すべての要素は30より大か? v.every(value => value > 30); // i=false
●2次元Obj配列の場合
配列 m = [
{氏名:"阿部", 学部:"文学部", 年齢:20},
{氏名:"井上", 学部:"工学部", 年齢:20},
{氏名:"宇野", 学部:"文学部", 年齢:21}
];
すべての学生が文学部か? i = m.every(学生 => 学生.学部 === '文学部'); // i=false
全ての学生は、年齢 >= 20 か? i = m.every(学生 => 学生.年齢 >= 20); // i=true
(注)ここでの「学生」は「各行」の意味。名称は自由て事前定義は不要)
学生.学部 === '文学部' は == でも正しく動くが、厳密には === を用いる
テスト
テストソース表示
some 条件をみたす要素が一つでもあるか
●1次元配列の場合
配列 v = [10, 20, 40, 40, 50];
30より大の要素があるか? i = v.some(value => value > 30); // i=true
25の要素はあるか? i = v.some(value => value === 25); // i=false
●2次元Obj配列の場合
配列 m = [
{氏名:"阿部", 学部:"文学部", 年齢:20},
{氏名:"井上", 学部:"工学部", 年齢:20},
{氏名:"宇野", 学部:"文学部", 年齢:21}
];
文学部 の学生がいるか? i = m.some(学生 => 学生.学部 === '文学部'; // i=true
年齢 < 20 の学生がいるか? i = m.some(学生 => 学生.年齢 < 20); // i=false
(注)ここでの「学生」は「各行」の意味。名称は自由て事前定義は不要)
学生.学部 === '文学部' は == でも正しく動くが、厳密には === を用いる
テスト
テストソース表示
slice / splice
slice 配列の部分抽出
一般形;新配列 = 元配列.slice(最初位置, 最後位置-1) // -1 に注意
元配列 v = [10,20,30,40,50];
v1 = v.slice(1, 3); // v1=[20, 30] v[1], v[2] を取り出す。v[3]は取り出さない
// v=[10,20,30,40,50] 元配列は変化しない
v1 = v.slice(2); // v1=[30,40,50] 第2引数を省略すると元配列の最後までの指定になる
v1 = v.slice(1, -2); // v1=[20,30] 第2引数を負数にすると元配列の最後からの指定になる
1次元配列では、抽出後の変更は他に影響を与えない
v1 = v.slice(1, 3)後に v[1] = 200; v1[1] = 300;
v=[10,200,30,40,50] v1=[20,300]
2次元配列のとき
元配列 m = [[0, 1, 2], [10, 11, 12], [20, 21, 22],[30, 31, 32], [40, 41, 42], [50, 51, 52]];
m1 = m.slice(1, 3); // m1=[ [10,11,12], [20,21,22] ]
2次元配列のときは m,m1 の変更は他に反映される m[1][0] = 100; m1[1][0] = 200;
m = [[0, 1, 2], [100, 11, 12], [200, 21, 22],[30, 31, 32], [40, 41, 42], [50, 51, 52]]; 元配列 m = [];
m1=[ [100,11,12], [200,21,22] ]
テスト
テストソース表示
splice 配列の部分抽出。抽出した部分は元配列から削除
一般形;新配列 = 元配列.splice(最初位置, 要素数)
配列 v=10,20,30,40,50
v1 = v.splice(1, 3) // v1=[20,30,40] v=[10,50]
第2引数を省略すると元配列の最後までの指定になる
v1 = v.splice(2) // v1=[30,40,50] v=[10,20]
2次元配列のとき
元配列 m = [[0, 1, 2], [10, 11, 12], [20, 21, 22],[30, 31, 32], [40, 41, 42], [50, 51, 52]];
m1 = m.splice(1, 3); // m1=[ [10,11,12], [20,21,22], [30,31,32]]
// m =[ [ 0, 1, 2], [40,41,42], [50,51,52]]
(m と m1 は重複しないので、一方の更新は他に影響sじない)
テスト
テストソース表示
join 配列の全要素を連結した文字列を返す
一般形;新配列 = 元配列.join(区切り文字)
配列 v = ["A", "B", "C", "D", "E"];
str = v.join('/') // str="A/B/C/D/E"
str = v.join(); // str="A,B,C,D,E"
str = v.join('') // str="ABCDE"
テスト
テストソース表示
split 区切文字で配列にする
一般形;配列 = 文字列.split(区切り文字)
str = "A, B, C"; v = str.split(',');
// v[0]="A"; v[1]=" B"; v[2]=" C" 空白も入れられる
str ="A B C"; v = str.split(' '); // 「半角スペース」で分離
// v[0]="A"; v[1]="B"; v[2]=""; v[3]="C"
// 連続した空白があると、その間に '' があるとして配列に入れられる
str.split(元文字).join(新文字); join と組み合わせて置換する。
str ="A B C";
str1 = str.split(' ').join('*'); // 空白→*
// str1="A*B**C"
テスト
テストソース表示
keys / values / entries
Ojb配列の key / value / そのペアの一覧
1次元Obj配列のとき
入力
v = {氏名:"阿部", 学部:"文学部", 年齢:20};
キー表 = Object.keys(v) // キー表 = 氏名,学部,年齢
// キー表[0]=氏名
値表 = Object.values(v) // 値表 = 阿部,文学部,20
// 値表[0]=阿部
対応表 = Object.entries(v) // 対応表=氏名,阿部,学部,文学部,年齢,20
// 対応表[0]=氏名,阿部
// 対応表[0][0]=氏名, 対応表[0][1]=阿部
2次元Obj配列のとき
入力
var m = [
{氏名:"阿部", 学部:"文学部", 年齢:20},
{氏名:"井上", 学部:"工学部", 年齢:20}
];
対象行を指定すれば1次元と同じ
キー表 = Object.keys(m[0]) // キー表=氏名,学部,年齢
// キー表[0]=氏名
★応用;obj配列から列名行つき通常配列を作成
var 配列 = []; // 配列 = [
配列[0] = Object.keys(m[0]); // [氏名,学部,年齢], ←列名行
for (var i=0; i<m.length; i++) { // [阿部,文学部,20],
配列[i+1] = Object.values(m[i]); // [井上,工学部,20],
} // ];
テスト
テストソース表示