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 に絵文字表現を挿入できない問題の解決方法

推薦する

MySQL がユーザー名とパスワードの漏洩を引き起こす可能性のある Riddle の脆弱性を公開

MySQL バージョン 5.5 および 5.6 を標的とする Riddle 脆弱性により、中間者攻撃...

Linux システムで crontab を使用して MySQL データベースを定期的にバックアップする方法

システムの crontab を使用して定期的にバックアップ ファイルを実行し、バックアップ結果を日付...

MySQL 8.0.18 のインストールと設定方法のグラフィックチュートリアル (Windows 10 版)

この記事は、参考のためにMySQL 8.0.18のインストールと設定のグラフィックチュートリアルを記...

Linuxオンラインソフトウェアgccをオンラインでインストールする方法

Linux オンラインインストール関連コマンド: yum install: すべてインストールyum...

MySQL における in と exists の違いの詳細な説明

1. 事前に準備する便宜上、ここで 2 つのテーブルを作成し、そこにいくつかのデータを追加します。果...

Vueでeslintを使用する方法の詳細な説明

目次1. 説明2. 関連する依存パッケージをダウンロードする3. 設定ファイル .eslintrc....

CSSを使用して3Dフォトウォール効果を作成する

CSS を使用して 3D フォト ウォールを作成します。具体的なコードは次のとおりです。 <!...

HTML メタタグの一般的な使用例のコレクション

マタタグとは<meta> 要素は、検索エンジン向けの説明やキーワード、更新頻度など、ペー...

docker を使用してコード サーバーをデプロイする方法

画像をプルする # docker pull codercom/code-server # Docke...

Tomcat で複数の war パッケージを展開する方法と手順

1 背景JDK1.8-u181とTomcat8.5.53がインストールされました。インストール後、環...

Zabbix パスワードをリセットする方法 (ワンステップ)

問題の説明長い間アカウントパスワードを入力して Zabbix にログインしていないため、管理者パスワ...

Webpack5-react スキャフォールディングをゼロから構築するための実装手順 (ソースコード付き)

目次ウェブパック5公式スタート建築ガイド構築を開始する依存する準備が完了したら、プロジェクトの構築を...

nginx+FastDFS を使ってファイル管理システムを段階的に構築する

目次1. FastDFS の概要1. はじめに2. FastDFSストレージ戦略3. FastDFS...

Vueのライブ放送機能の詳しい説明

最近、会社でたまたま生放送をしていたのですが、今日は私が遭遇した落とし穴を記録します。会社のサーバー...

MySQLで大きなテーブルを正常に削除する方法の詳細な説明

序文テーブルを削除するには、無意識に思い浮かぶコマンドは、DROP TABLE "テーブル...