序章

JavaScriptには、配列を操作するためのいくつかの関数が含まれています。 for ループ。 自分のプロジェクトでこれらの関数を使用したことがあり、それらがどのように機能するのか、なぜ相互に使用するのか疑問に思ったことがあるかもしれません。

何かがどのように機能するかを理解するための優れた方法は、独自のバージョンを最初から作成することです。 この記事では、独自のバージョンを作成してこれを行います。 map, filter, sort、 と reduce ゼロから。 完了すると、これらの関数とその使用法について理解を深めることができます。

ES6の矢印関数とJavaScriptの配列関数を組み合わせることで、非常に強力でクリーンなコードを記述できます。

JavaScript配列メソッドのしくみ

例から始めましょう。 数値の配列を反復処理し、各要素を1ずつインクリメントして、新しい配列を返すとします。 以前は、これを実現するためにいくつかのことを行う必要がありました。

  • 新しい空のアレイを初期化します。
  • 元の配列の各要素を繰り返し処理します。
  • その要素を変更し、変更した値を新しい配列に配置します。

コードは次のようになります。

const arr = [1, 2, 3];
const newArray = [];

for (let i = 0; i < arr.length; i++) {
    newArray[i] = arr[i] + 1;
}

return newArray;

しかし、ビルトインで map 関数の場合、これは1行のコードで実行できます。

return arr.map(element => ++element);

JavaScript配列メソッドは、ES6Arrow関数を多用します。

ここで取り上げる各配列関数は、関数をパラメーターとして受け入れます。 配列の各要素を反復処理し、その関数を呼び出して、各要素をどう処理するかを決定します。 各要素を反復処理してコールバック関数を呼び出した後、新しい配列またはアイテムが返されます。

前提条件

このチュートリアルをローカルで実行するには、エディター( Visual Studio Code など)とサンドボックス環境拡張機能( Quokka.js など)が必要です。

オンラインでチュートリアルをフォローするには、CodePenまたはCodeSandboxを使用できます。

ステップ1—マップの実装

map 各要素を反復処理し、何らかの方法で変換し、新しい配列に追加して、新しい配列を返します。

警告:この記事では、カスタム関数を使用してJavaScriptグローバルオブジェクトを拡張します。 この方法は本番コードに副作用をもたらす可能性があるため、これは教育目的のみです。

JavaScript配列関数は、たとえばJavaのクラスの関数と同様に、配列プロトタイプの一部です。 それらをオーバーライドするには、新しい関数をに割り当てることができます Array.prototype.

新しい関数を作成して、に割り当てましょう Array.prototype.mapFromScratch:

const myCustomMapFunction = function(callback) {
    console.log('My Custom Map Function!');
}
Array.prototype.mapFromScratch = myCustomMapFunction;

const arr = [1, 2, 3];

arr.mapFromScratch();

このコードを実行すると、コンソールにログメッセージが表示されます。

Output
My Custom Map Function!

ここで、 for 各要素をループして印刷します。 配列自体がメソッドを呼び出すため、参照することでその配列にアクセスできます this:

const myCustomMapFunction = function(callback) {
    console.log('My Custom Map Function!');

    // 'this' refers to the array
    for (let i = 0; i < this.length; i++) {
        console.log(this[i]);
    }

}

次に、コールバック関数を呼び出して、必要な変換を実行します。 その場合、現在の要素と現在のインデックスを渡します。

const myCustomMapFunction = function(callback) {
    console.log('My Custom Map Function!');

    // 'this' refers to the array
    for (let i = 0; i < this.length; i++) {
        const transformedElement = callback([this[i], i);
    }

}

最後に、変換された要素を新しい配列に追加し、その配列を返します。

const myCustomMapFunction = function(callback) {
    console.log('My Custom Map Function!');

    const newArray = [];

    // 'this' refers to the array
    for (let i = 0; i < this.length; i++) {
        newArray[i] = callback(this[i], i);
    }

    return newArray;
}

配列内の各値をインクリメントする関数でテストして、書き換えられた関数の動作を見てみましょう。

// arr = [1, 2, 3]
// expected = [2, 3, 4]

console.log(arr.mapFromScratch((element) => ++element));

次の出力が表示されます。

Output
My Custom Map Function! [2, 3, 4]

このステップでは、カスタムを実装しました map 関数。 次に、実装を検討しましょう filter 関数。

ステップ2—フィルターの実装

The filter 関数は、元の配列からフィルタリングされた要素の新しい配列を返します。

新しい関数を作成して、に割り当てましょう Array.prototype.filterFromScratch:

const myCustomFilterFunction = function(callback) {
    console.log('My Custom Filter Function!');
}
Array.prototype.filterFromScratch = myCustomFilterFunction;

const arr = [1, 2, 3];

arr.filterFromScratch();

次に、 for 各要素を反復処理するためのループ:

const myCustomFilterFunction = function(callback) {
    console.log('My Custom Filter Function!');

    const newArray = [];

    for (let i = 0; i < this.length; i++) {
        console.log(this[i]);
    }
}

の内部 for ループでは、各要素を新しい配列に追加するかどうかを決定する必要があります。 これがコールバック関数の目的であるため、条件付きで各要素を追加するために使用します。 戻り値が true、要素を戻り配列にプッシュします。

const myCustomFilterFunction = function(callback) {
    console.log('My Custom Filter Function!');

    const newArray = [];

    for (let i = 0; i < this.length; i++) {
        if (callback(this[i])) {
            newArray.push(this[i]);
        }
    }

    return newArray;
}

より大きい値を表示する関数でテストすることにより、実際に書き直された関数を見てみましょう。 1:

// arr = [1, 2, 3]
// expected = [2, 3]

console.log(arr.filterFromScratch((element) =>  element > 1));

次の出力が表示されます。

Output
My Custom Filter Function! [2, 3]

これで、カスタムを実装しました filter 関数。 次に、 sort 関数。

ステップ3—ソートの実装

The sort 関数は、元の配列からソートされた配列を返します。

新しい関数を作成して、に割り当てましょう Array.prototype.sortFromScratch:

const myCustomSortFunction = function(callback) {
    console.log('My Custom Sort Function!');
}
Array.prototype.sortFromScratch = myCustomSortFunction;

const arr = [3, 2, 1];

arr.sortFromScratch();

このソートの実装には、バブルソートを使用します。 これが私たちが取るアプローチです:

  • 配列内のアイテムを繰り返し繰り返します。
  • 隣接するアイテムを比較し、順序が正しくない場合は交換します。
  • 各比較を行うのに十分な回数配列を反復処理した後、配列がソートされます。

バブルソートでは、配列内の要素ごとに1回配列を完全に反復処理する必要があります。 これはネストされたものを必要とします for 内側のループが最後の要素の1つ手前で停止することを繰り返すループなので、ここで追加しましょう。

:これは教育目的であり、効率的な並べ替え方法ではありません。

const myCustomSortFunction = function(callback) {
    console.log('My Custom Sort Function!');

    const newArray = [];

    for (let i = 0; i < newArray.length; i++) {
        for (let j = 0; j < newArray.length - 1; j++) { 
        }
    }
}

また、元の配列を変更する必要はありません。 これを回避するには、 SpreadOperatorを使用して元の配列を新しい配列にコピーできます。

const myCustomSortFunction = function(callback) {
    console.log('My Custom Sort Function!');

    const newArray = [...this];

    for (let i = 0; i < newArray.length; i++) {
        for (let j = 0; j < newArray.length - 1; j++) { 
        }
    }
}

コールバック関数は、現在の要素と次の要素の2つのパラメーターを受け取り、それらが正しいかどうかを返します。 私たちの場合、コールバック関数がより大きい数を返す場合 0、2つの要素を交換します。

const myCustomSortFunction = function(callback) {
    console.log('My Custom Sort Function!');

    const newArray = [...this];

    for (let i = 0; i < newArray.length; i++) {
        for (let j = 0; j < newArray.length - 1; j++) {
            if (callback(newArray[j], newArray[j + 1]) > 0) {
                // swap the elements
            }
        }
    }
}

要素を交換するには、1つのコピーを作成し、最初の要素を置き換えてから、2番目の要素をコピーに置き換えます。 終了すると、新しくソートされた配列が返されます。

const myCustomSortFunction = function(callback) {
    console.log('My Custom Sort Function!');

    const newArray = [...this]; 

    for (let i = 0; i < newArray.length; i++){
        for (let j = 0; j < newArray.length - 1; j++) {
            if (callback(newArray[j], newArray[j + 1]) > 0) {
                const temp = newArray[j + 1];
                newArray[j + 1] = newArray[j];
                newArray[j] = temp;
            }
       }
    }

    // array is sorted
    return newArray;
}

低から高にクイックソートする関数でテストして、書き直した関数の動作を見てみましょう。

// arr = [3, 2, 1]
// expected = [1, 2, 3]

console.log(arr.sortFromScratch((current, next) => current - next));

次の出力が表示されます。

Output
My Custom Sort Function! [1, 2, 3]

これで、カスタムを作成できました sort 関数、あなたは実装に移る準備ができています reduce 関数。

ステップ4—Reduceの実装

The reduce 関数は各要素を反復処理し、1つの単一の値を返します。

reduce 他の関数のように新しい配列を返しません。 実際には、配列内の要素を1つの最終値(数値、文字列、またはオブジェクト)に「削減」します。 使用する最も一般的な理由の1つ reduce 数値の配列内のすべての要素を合計する場合に使用します。

新しい関数を作成して、に割り当てましょう Array.prototype.reduceFromScratch:

const myCustomReduceFunction = function(callback) {
    console.log('My Custom Reduce Function!');
}
Array.prototype.reduceFromScratch = myCustomReduceFunction;

const arr = [1, 2, 3];

arr.reduceFromScratch();

為に reduce 1つの最終値を返すには、操作する開始値が必要です。 ユーザーが渡すコールバック関数は、配列の各要素に基づいてこのアキュムレータを更新し、最後に返す方法を決定します。 コールバック関数は、更新されたアキュムレータを返す必要があります。

あなたの for ここでループし、コールバック関数を呼び出します。 戻り値が新しいアキュムレータになります。 ループが終了したら、アキュムレータを返します。

const myCustomReduceFunction = function(callback, accumulator) {
    console.log('My Custom Reduce Function!');
    for (let i = 0; i < this.length; i++) {
        accumulator = callback(accumulator, this[i]);
    }
    return accumulator;
}

配列の内容を合計する関数でテストして、書き直した関数の動作を見てみましょう。

// arr = [1, 2, 3]
// expected = 6

console.log(arr.reduceFromScratch((accumulator, element) => accumulator + element, 0));
Output
My Custom Reduce Function! 6

結論

JavaScriptの配列関数は非常に便利です。 このチュートリアルでは、配列関数を再実装して、それらがどのように機能するかをよりよく理解し、それらをより効果的に使用できるようにしました。