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 8.0はJSONを扱えるようになりました

目次1. 概要2. JSON基本ツール3. JSONパス式4. JSONを検索して変更する序文:長い...

よく使われる HTML 形式のタグ_Powernode Java Academy

1. タイトルHTML では、<h1></h1> から <h6>...

デザイン理論: なぜ私たちは間違った場所を見ているのでしょうか?

数日前、バスで仕事に行きました。バスのカードリーダーの実際の使用シーンを実際に見て、カードリーダーの...

JavaScript フロー制御 (分岐)

目次1. プロセス制御2. シーケンシャルプロセス制御3. 分岐フロー制御if文1. 支店構造2. ...

Linux における「/」と「~」の違いの詳細な説明

「/」はルートディレクトリ、「~」はホームディレクトリです。 Linux ストレージはツリー状にマウ...

シンプルなカルーセル効果を実現するJavaScript

カルーセルとは何ですか?カルーセル: モジュールまたはウィンドウで、コンピューターでマウスをクリック...

Dockerfile に基づいて Tomcat イメージを構築する方法

Dockerfile は Docker イメージを構築するために使用されるファイルです。コマンドパラ...

MySQLパーティションテーブルは月別に分類されています

目次テーブルを作成するデータベース ファイルを表示します。入れるクエリ消去補足:Mysqlは月テーブ...

React useEffect の理解と使用

目次繰り返しレンダリングループを避ける副作用の除去についてReact16.8 の新しい useEff...

ウェブデザインの概要

<br />1998年に最初の個人ページが誕生してから2008年の今日まで、デザイン業界...

Zabbix上のすべてのホストのIPとホスト名を取得する

ザビックスZabbix ([`zæbiks]) は、WEB インターフェースに基づいて分散システム監...

MySQL コマンドラインモードアクセス操作 MySQL データベース操作

使用環境cmd モードで、mysql --version と入力します (インストールされている M...

入力選択スタイルを変更する CSS 疑似クラスのサンプルコード

注: この表はW3Schoolチュートリアルから引用したものです疑似要素の分類と機能: 入力選択スタ...

Linux カーネル デバイス ドライバー Linux カーネル 基本メモの概要

1. Linuxカーネルドライバモジュールの仕組み静的ロードでは、ドライバモジュールをカーネルにコン...

MySQL 8.0.15 インストール グラフィック チュートリアルとデータベースの基礎

MySQLソフトウェアのインストールとデータベースの基礎は参考用です。具体的な内容は次のとおりです。...