JavaScript Reduceの詳しい説明

JavaScript Reduceの詳しい説明

このスキルReduce学ぶことで、プログラミングの新しい世界が開けます

このReduceスキルを習得すると、まったく新しい世界が開けます🎉

reducemap / filter / some / everyなどの他の配列メソッドを置き換えることができるため、最も柔軟な JS 配列メソッドです。また、理解するのが最も難しいメソッドでもあります。多くの lodash メソッドもこれを使用して実装できます。reduce を学習すると、開発者は、以前の手続き型または命令型の観点ではなく、問題を解決するための別の機能的かつ宣言的な観点を得ることができます。

難しい点の 1 つは、 acc ( accumulation ) の型を決定し、初期値を選択する方法です。実際、適切な初期値を見つけるのに役立つちょっとしたコツがあります。必要な戻り値の型は、 accの型と同じである必要があります。たとえば、合計の最終結果が数値である場合、 acc数値型である必要があるため、その初期化は0でなければなりません。

まず、 reduceの理解と使用方法を強化することから始めましょう。

地図

ヒントによると、 mapの最終的な戻り値は配列なので、 accも配列である必要があり、初期値として空の配列を使用できます。

/**
 * 組み込みの `Array.prototype.map` メソッドを実装するには `reduce` を使用します。
 * @param {any[]} 引数 
 * @param {(val: any, index: number, thisArray: any[]) => any} マッピング 
 * @returns {any[]}
 */
関数 map(arr, マッピング) {
 arr.reduce((acc, item, index) => [...acc, マッピング(item, index, arr)], []); を返します。
}

テスト

map([null, false, 1, 0, '', () => {}, NaN], val => !!val);

// [偽、偽、真、偽、偽、真、偽]

フィルター

ヒントによると、 filterの最終的な戻り値も配列なので、 accも配列である必要があり、空の配列も使用できます。

/**
 * 組み込みの `Array.prototype.filter` メソッドを実装するには、`reduce` を使用します。
 * @param {any[]} 引数 
 * @param {(val: any, index: number, thisArray: any[]) => boolean} 述語 
 * @returns {any[]}
 */
関数フィルター(arr, 述語) {
 arr.reduce((acc, item, index) => predicate(item, index, arr) ? [...acc, item] : acc, []); を返します。
}

テスト

フィルター([null, false, 1, 0, '', () => {}, NaN], val => !!val);

// [1, () => {}]

いくつかの

ターゲット配列が空の場合、 some falseを返すため、初期値はfalseになります。

関数 some(arr, 述語) {
 arr.reduce((acc, val, idx) => acc || predicate(val, idx, arr), false) を返します
}

テスト:

いくつか([null, false, 1, 0, '', () => {}, NaN], val => !!val);
// 真実

いくつか([null, false, 0, '', NaN], val => !!val);
// 間違い

念のため、この 2 つは結果には影響しませんが、パフォーマンスに違いがあります。acc を前に置くと短絡アルゴリズムとなり、不要な計算を回避できるため、パフォーマンスが向上します。

acc || 述語(val, idx, arr)

そして

述語(val, idx, arr) || acc


everyターゲット配列が空の場合はtrueを返すので、初期値はtrueになります。

関数 every(arr, 述語) {
 arr.reduce((acc, val, idx) => acc && predicate(val, idx, arr), true) を返します。
}

インデックスを検索

findIndexターゲット配列が空の場合は -1 を返すため、初期値は -1 になります。

関数 findIndex(arr, 述語) {
 定数 NOT_FOUND_INDEX = -1;

 arr.reduce((acc, val, idx) => { を返します
 (acc === NOT_FOUND_INDEX)の場合{
 述語(val, idx, arr)を返します? idx: NOT_FOUND_INDEX;
 }
 
 acc を返します。
 }, インデックスが見つかりません)
}

テスト

findIndex([5, 12, 8, 130, 44], (要素) => 要素 > 8) // 3

パイプ

1. 以下の機能を実装する

/**
 * 入力値を左から右に順番に提供された関数で処理する関数を返します。
 * @param {(funcs: any[]) => any} 関数 
 * @returns {(arg: any) => any}
 */
関数パイプ(...funcs) {}

作る

パイプ(val => val * 2, Math.sqrt, val => val + 10)(2) // 12

この関数は、より複雑な処理を実装するために使用できます。

// 値が正である項目を取り出し、その値に 0.1 を掛け、すべての項目の値を合計して、最終的に 3 を取得します。
const プロセス = パイプ(
 arr => arr.filter(({ val }) => val > 0)、 
 arr => arr.map(item => ({ ...item, val: item.val * 0.1 })), 
 arr => arr.reduce((acc, { val }) => acc + val, 0)
);

プロセス([{ val: -10 }, { val: 20 }, { val: -0.1 }, { val: 10 }]) // 3

2. 上記のパイプの機能を実現し、不特定多数のパラメータを受け入れる関数を返す次の関数を実装します。

/**
 * 入力値を左から右へ順に提供された関数で処理する関数を返します。
 * @param {(funcs: any[]) => any} 関数 
 * @returns {(args: any[]) => any}
 */
関数パイプ(...funcs) {}

次の単一のテストに合格する

パイプ(合計、Math.sqrt、val => val + 10)(0.1、0.2、0.7、3) // 12

その中で、 sumが実現されました

/**
 * 数字を合計します。
 * @param 引数番号[]
 * 合計を {number} で返します。
 */
関数 sum(...args) {
 args.reduce((a, b) => a + b) を返します。
}

参考回答

1. パラメータを受け入れる関数を返す

非関数をフィルタリングするfuncステップを省略する

/**
 * 入力値を左から右の順に提供された関数で処理する関数を返します。
 * @param {(arg: any) => any} 関数
 * @returns {(arg: any) => any}
 */
関数パイプ(...funcs) {
 戻り値 (引数) => {
 関数reduce()を返す
 (acc, 関数) => 関数(acc)、
 引数
 )
 }
}

2. 戻り関数は不定パラメータを受け入れる

非関数を除外する func ステップも省略されます。

/**
 * 入力値を左から右に順番に提供された関数で処理する関数を返します。
 * @param {Array<(...args: any) => any>} 関数
 * @returns {(...args: any[]) => any}
 */
関数パイプ(...関数) {
	// 定数 realFuncs = funcs.filter(isFunction);

 戻り値 (...引数) => {
 関数reduce()を返す
 (acc, func, idx) => idx === 0 ? func(...acc) : func(acc),
 引数
 )
 }
}

不要な比較やCPUの浪費を回避し、書き込みパフォーマンスを向上

関数パイプ(...関数) {
 戻り値 (...引数) => {
 // 最初のものは処理済みなので、残りのものだけ処理する必要があります return funcs.slice(1).reduce(
 (acc, 関数) => 関数(acc)、
 
 // まず、`acc` として特別なケースを処理します
 関数[0](...引数)
 )
 }
}

2番目の書き方であるfuncs[0](...args)の落とし穴に注意してください。配列が空の場合、ヌルポインターのために爆発してしまいます。

lodash.get の実装

getを実装すると、次の例では'hello world'が返されます。

const obj = { a: { b: { c: 'hello world' } } };

'abc' を取得します。

関数シグネチャ:

/**
 * キーパスで値を取得する
 * @param 任意のオブジェクト
 * @param keyPath ドットで区切られたキーパス文字列* @returns {any} ターゲット値*/
関数 get(obj, keyPath) {}

参考回答

/**
 * キーパスで値を取得します。
 * @param 任意のオブジェクト
 * @param keyPath ドットで区切られたキーパス文字列* @returns {any} ターゲット値*/
関数 get(obj, keyPath) {
 もし (!obj) {
 未定義を返します。
 }

 keyPath.split('.').reduce((acc, key) => acc[key], obj); を返します。
}

lodash.flattenDeep の実装

concat 演算子と spread 演算子は 1 つのレイヤーのみを平坦化できますが、再帰によって深い平坦化を実現できます。

方法1: スプレッド演算子

関数flatDeep(arr) {
 arr.reduce((acc, item) を返す => 
 Array.isArray(item) ? [...acc, ...flatDeep(item)] : [...acc, item],
 []
 )
}

方法2: 連結

関数flatDeep(arr) {
 arr.reduce((acc, item) を返す => 
 acc.concat(Array.isArray(item) ? flatDeep(item) : item),
 []
 )
}

興味深いパフォーマンス比較: 展開演算子は 70,000 回実行するのに 1098 ミリ秒かかりますが、concat は同じ時間内に 20,000 回しか実行できません。

関数flatDeep(arr) {
 arr.reduce((acc, item) を返す => 
 Array.isArray(item) ? [...acc, ...flatDeep(item)] : [...acc, item],
 []
 )
}

var arr = repeat([1, [2], [[3]], [[[4]]]], 20);

コンソールにログ出力します。
コンソールにログ出力します。

コンソール.time('concat')
(i = 0; i < 7 * 10000; ++i) の場合 {
 フラットディープ(arr)
}
console.timeEnd('concat')

関数 repeat(arr, times) { let result = []; for (i = 0; i < times; ++i) { result.push(...arr) } return result; }

オブジェクト内のnull値を除外する

成し遂げる

クリーン({ foo: null, bar: undefined, baz: 'hello' })

// { baz: 'こんにちは' }

答え

/**
 * `nil` (null または undefined) 値を除外します。
 * @param {オブジェクト} obj
 * @returns {any}
 *
 * @example clean({ foo: null, bar: undefined, baz: 'hello' })
 *
 * // => { baz: 'hello' }
 */
エクスポート関数 clean(obj) {
 もし (!obj) {
 obj を返します。
 }

 Object.keys(obj).reduce((acc, key) => { を返します。
 if (!isNil(obj[key])) {
 acc[キー] = obj[キー];
 }

 acc を返します。
 }, {});
}

列挙する

定数オブジェクトをTS列挙体としてシミュレートする

enumifyを実装して

const 方向 = {
 アップ: 0,
 ダウン: 1,
 左: 2、
 右: 3、
};

const 実際 = enumify(方向);

定数期待値 = {
 アップ: 0,
 ダウン: 1,
 左: 2、
 右: 3、

 0: 「上」、
 1: 「ダウン」、
 2: 「左」、
 3: 「正しい」
};

deepStrictEqual(実際、期待値);

答え:

/**
 * オブジェクトから列挙型を生成します。
 * https://www.typescriptlang.org/play?#code/KYOwrgtgBAglDeAoKUBOwAmUC8UCMANMmpgEw5SlEC+UiiAxgPYgDOTANsAHQdMDmAChjd0GAJQBuRi3ZdeA4QG08AXSmIgA を参照してください
 * @param {オブジェクト} obj
 * @returns {オブジェクト}
 */
エクスポート関数enumify(obj) {
 if (!isPlainObject(obj)) {
 throw new TypeError('enumify ターゲットはプレーン オブジェクトである必要があります');
 }

 Object.keys(obj).reduce((acc, key) => { を返します。
 acc[キー] = obj[キー];
 acc[obj[key]] = キー;

 acc を返します。
 }, {});
}

Promise シリアルエグゼキュータ

Reduce を使用すると、不特定多数の Promise を連続して実行することができ、実際のプロジェクトで大きな役割を果たすことができます。ここでは詳細には触れませんので、次の記事「JS リクエスト スケジューラ」を参照してください。

拡大する

テスト フレームワークとして jest を使用し、この記事のすべてのメソッドの単体テストを記述してください。その他の演習については、github.com/you-dont-ne… を参照してください。

上記はJavaScript Reduceの使い方を詳しく説明した内容です。JavaScript Reduceの使い方についてさらに詳しく知りたい方は、123WORDPRESS.COMの他の関連記事もぜひご覧ください!

以下もご興味があるかもしれません:
  • JavaScript における Reduce() の 5 つの基本的な使用例
  • JavaScript 配列のreduce() メソッド
  • 8 JSのreduce使用例とreduce操作方法
  • JS での Reduce() メソッドの使用の概要
  • jsはreduceメソッドを使用してコードをよりエレガントにします
  • JavaScriptのreduceの基本的な使い方を詳しく説明します

<<:  Packetdrillの簡潔なユーザーガイド

>>:  MySQL に絵文字表現を挿入できない問題の解決方法

推薦する

UbuntuでOpenCVをコンパイルしてインストールする方法

opencv2 の簡単なインストール: conda インストール --channel https:/...

LED を使って Linux カーネルを使い始める方法を探る

目次序文LEDトリガー探索を始めるLEDデバイス登録LEDディレクトリ類推によって理解するクラスディ...

優れた登録プロセスの手順

ウェブサイトにとって、これは最も基本的な機能です。それでは、登録プロセスに含まれる手順を見てみましょ...

JS で async と await を使用する方法

目次1. 非同期2. 待つ: 3. 包括的なアプリケーション1. 非同期async 、非同期コードが...

JDBC-idea で mysql をインポートして java jar パッケージに接続する (mac)

序文1. この記事ではMySQL 8.0バージョンを使用していますバージョン5.0と比較すると、パッ...

VMware 仮想マシンに CentOS と Qt をインストールするチュートリアル図

VMware のインストールパッケージのインストールダウンロードアドレス: https://www....

ミニプログラムカスタムコンポーネントの非効率的なグローバルスタイルの解決策

目次長すぎて読めないコンポーネントスタイルの分離デモテスト優先度ページの分離構成参考文献ネイティブ ...

Vueのスロットの詳細な説明

Vue でのコードの再利用により、mixnis が提供されます。テンプレートの再利用により、スロット...

CSS インライン スタイル、埋め込みスタイル、外部参照スタイルを使用する 3 つの方法

3 つの方法を使用する簡単な例は次のとおりです。インラインスタイル: <!doctypehtm...

位置のいくつかの巧妙な応用の詳細な説明:sticky スティッキーポジショニング

背景: position:sticky はスティッキー配置とも呼ばれます。スティッキー配置の要素は、...

nginx でネストされた if メソッドを実装する方法

Nginx はネストされた if ステートメントをサポートしておらず、if ステートメントでの論理判...

Vueは画像のドラッグと並べ替えを実装します

この記事の例では、画像のドラッグと並べ替えを実装するためのVueの具体的なコードを参考までに共有して...

HTML 要素に注釈を付けるときにクラスと ID のどちらが優れているかを分析する

Web ページには、非常に複雑な HTML 構造があります。CSS を使用して関連するスタイルを定義...

Nginx ポート競合を解決するトラブルシューティング方法の例

問題の説明データ転送に Nginx を使用し、フロントエンドとバックエンドが分離された Spring...

MySQL トリガーの原理と使用例の分析

この記事では、例を使用して、MySQL トリガーの原理と使用方法を説明します。ご参考までに、詳細は以...