こんにちは、ニッパチです。
javascriptでよく使う配列の組み込み関数、mapやfilterとか便利ですがどんな処理をするものなのか
自分なりに考えてみました。
最初
Arrayクラスを拡張したMyArrayクラスを定義
class MyArray extends Array {
// mapの中身を実装してみる
my_map() {
}
// filterの中身を実装してみる
my_filter() {
}
// reduceの中身を実装してみる
my_reduce() {
}
}
map
まずはmapから
ざっくりとはこんな感じになるかと
class MyArray extends Array {
// mapの中身を実装してみる
my_map(callback) {
let new_ary = [];
for (let i = 0; i < this.length; i++) {
let new_item = callback(this[i], i);
new_ary.push(new_item);
}
return new_ary;
}
// filterの中身を実装してみる
my_filter() {
}
// reduceの中身を実装してみる
my_reduce() {
}
}
// 使用するコールバック関数
function double(item) {
return item * 2;
}
function double2(item, index) {
return item * index;
}
let array = new MyArray(1,2,3);
let r = array.my_map(double)
let r2 = array.my_map(double2)
console.log(r)
// [2,4,6]
console.log(r2)
// [0,2,6]
mapと同じ結果となりました。しかし、
こちらからmapの使用を調べると、map自体の引数は2つでコールバックの引数は3つでした。
なんか多いな。僕の普段使いで考えるとmapにはコールバックしか渡さないし、コールバックの引数も多くて2つまでしか使わないのでその他の引数はどういう意味なのか調べるとします。
まず、引数の詳細は以下の通りです。
- callbackFn コールバック
- element
- index
- array mapが呼び出された配列
- thisArg(省略可) callbackFnを実行するときにthisとして使う値
thisArgはなんのことかさっぱりです。
ちょっと実験です。
let array = [1,2,3];
function test() {
return this;
}
let r = array.map(test);
console.log(r);
// 結果
Windowオブジェクトが3つ入った配列
let obj = {name: 'regrex'}
r = array.map(test, obj);
console.log(r);
// 結果
objが3つ入った配列
ちょっとわかったかも。
この辺も考慮して改造したmy_map
がこちら
class MyArray extends Array {
// mapの中身を実装してみる
my_map(callback, that = null) {
let new_ary = [];
if (that) {
callback = callback.bind(that)
}
for (let i = 0; i < this.length; i++) {
let new_item = callback(this[i], i, this);
new_ary.push(new_item);
}
return new_ary;
}
// filterの中身を実装してみる
my_filter() {
}
// reduceの中身を実装してみる
my_reduce() {
}
}
filter
続いてfilter。ドキュメントをみると引数はmapとまったく同じでした。
class MyArray extends Array {
// mapの中身を実装してみる
my_map(callback, that = null) {
// 省略
}
// filterの中身を実装してみる
my_filter() {
let new_ary = [];
if (that) {
callback = callback.bind(that)
}
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
new_ary.push(this[i]);
}
}
return new_ary;
}
// reduceの中身を実装してみる
my_reduce() {
}
}
let array = new MyArray(1,2,3);
let r = array.my_filter(x => x % 2);
console.log(r)
// [1,3]
奇数だけの配列が返ってきて期待通りの結果となりました。
reduce
最後にreduceです。こいつに関しては普段使っていても「あれ?これなんだっけ?」とすぐなってしまいます。
ドキュメントを確認すると、初期値が指定されていない時や配列の長さによって挙動を制御する必要があります。
class MyArray extends Array {
// mapの中身を実装してみる
my_map(callback, that = null) {
// 省略
}
// filterの中身を実装してみる
my_filter(callback, that = null) {
// 省略
}
// reduceの中身を実装してみる
my_reduce(callback, initialVal = null) {
if (this.length === 0 && initialVal === null) {
throw TypeError('エラー');
}
if (this.length === 1 && initialVal === null) {
return this[0];
}
let previousVal = this[0];
let currentVal = this[1];
let currentIndex = 1;
let result;
if (initialVal) {
previousVal = initialVal;
currentVal = this[0];
currentIndex = 0;
}
for (let i = currentIndex; i < this.length; i++) {
result = callback(previousVal, currentVal, currentIndex, this);
previousVal = result;
currentIndex += 1;
currentVal = this[currentIndex];
}
return result;
}
}
function test(previous, current, index, arr) {
return previous + current;
}
let array = new MyArray(1,2,3);
let r = array.my_filter(test);
console.log(r)
これで期待通りの結果となりました。