のa.x
各要素を合計したいとしarr
ます。
arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a.x + b.x})
>> NaN
ある時点で斧が未定義であると信じる原因があります。
以下は正常に動作します
arr = [1,2,4]
arr.reduce(function(a,b){return a + b})
>> 7
最初の例で何が間違っていますか?
のa.x
各要素を合計したいとしarr
ます。
arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a.x + b.x})
>> NaN
ある時点で斧が未定義であると信じる原因があります。
以下は正常に動作します
arr = [1,2,4]
arr.reduce(function(a,b){return a + b})
>> 7
最初の例で何が間違っていますか?
回答:
最初の繰り返しの後の数を返すと、そのプロパティを取得しようとしているyour're x
ある次のオブジェクトに追加することををundefined
し、関係する数学undefined
で結果をNaN
。
x
パラメータのxプロパティの合計を持つプロパティを含むオブジェクトを返すようにしてください:
var arr = [{x:1},{x:2},{x:4}];
arr.reduce(function (a, b) {
return {x: a.x + b.x}; // returns object with property x
})
// ES6
arr.reduce((a, b) => ({x: a.x + b.x}));
// -> {x: 7}
コメントから追加された説明:
各反復の戻り値は、次の反復で変数[].reduce
として使用されa
ます。
反復1: 、、a = {x:1}
に割り当てられた反復で2b = {x:2}
{x: 3}
a
反復2:a = {x:3}
、b = {x:4}
。
この例の問題は、数値リテラルを返すことです。
function (a, b) {
return a.x + b.x; // returns number literal
}
イテレーション1: 、、a = {x:1}
として次の反復でb = {x:2}
// returns 3
a
イテレーション2: a = 3
、b = {x:2}
リターンNaN
数値リテラルは3
(通常)と呼ばれる性質を持っていないx
、それはですので、undefined
と undefined + b.x
リターンNaN
とNaN + <anything>
常にありますNaN
明確化:マジックプリミティブを使用して数値のプリミティブを取得するために削減するオプションのパラメーターを渡すことはよりクリーンであるという考えに同意しないため、このスレッドの他のトップアンサーよりも自分の方法を優先します。書き込まれる行は少なくなりますが、読みにくくなります。
これを行うためのより明確な方法は、初期値を提供することです。
var arr = [{x:1}, {x:2}, {x:4}];
arr.reduce(function (acc, obj) { return acc + obj.x; }, 0); // 7
console.log(arr);
無名関数が初めて呼び出されたときに、で呼び出されて(0, {x: 1})
を返します0 + 1 = 1
。次回はで呼び出され(1, {x: 2})
て戻ります1 + 2 = 3
。次にで呼び出され(3, {x: 4})
、最終的にが返され7
ます。
reduce
。
TL; DR、初期値を設定
使い方な破壊を
arr.reduce( ( sum, { x } ) => sum + x , 0)
破壊せずに
arr.reduce( ( sum , cur ) => sum + cur.x , 0)
Typescriptを使用
arr.reduce( ( sum, { x } : { x: number } ) => sum + x , 0)
破壊法を試してみましょう:
const arr = [ { x: 1 }, { x: 2 }, { x: 4 } ]
const result = arr.reduce( ( sum, { x } ) => sum + x , 0)
console.log( result ) // 7
その鍵となるのが初期値の設定です。戻り値は次の反復の最初のパラメーターになります。
受け入れられた回答は、「オプション」の値を渡さないことを提案しています。これは誤りです。慣用的な方法では、2番目のパラメーターが常に含まれるからです。どうして?3つの理由:
1.危険 -初期値を渡さないことは危険であり、コールバック関数が不注意な場合、副作用や変異を引き起こす可能性があります。
見よ
const badCallback = (a,i) => Object.assign(a,i)
const foo = [ { a: 1 }, { b: 2 }, { c: 3 } ]
const bar = foo.reduce( badCallback ) // bad use of Object.assign
// Look, we've tampered with the original array
foo // [ { a: 1, b: 2, c: 3 }, { b: 2 }, { c: 3 } ]
ただし、初期値を使用してこの方法で行った場合:
const bar = foo.reduce( badCallback, {})
// foo is still OK
foo // { a: 1, b: 2, c: 3 }
レコードについては、元のオブジェクトを変更する予定がない限り、最初のパラメーターをObject.assign
空のオブジェクトに設定します。このように:Object.assign({}, a, b, c)
。
2-型推論の 改善-TypescriptのようなツールやVS Codeのようなエディターを使用すると、コンパイラーにイニシャルを伝えるメリットが得られ、間違っているとエラーをキャッチできます。初期値を設定しないと、多くの状況でそれを推測できず、不気味なランタイムエラーが発生する可能性があります。
3-ファンクタを尊重する
-JavaScriptは、その内部の機能的な子が解放されたときに最もよく輝きます。機能的な世界では、「折りたたみ」またはreduce
配列の方法に関する標準があります。配列にカタモフィズムをフォールドまたは適用すると、その配列の値を取得して新しいタイプを構築します。結果の型を伝達する必要があります。これは、最終的な型が配列、別の配列、またはその他の型の値の型であっても行う必要があります。
別の方法で考えてみましょう。JavaScriptでは、関数はデータのように渡すことができます。これがコールバックのしくみです。次のコードの結果はどうなりますか?
[1,2,3].reduce(callback)
数値を返しますか?オブジェクト?これにより明確になります
[1,2,3].reduce(callback,0)
関数型プログラミング仕様について詳しくは、https://github.com/fantasyland/fantasy-land#foldableをご覧ください。
このreduce
メソッドは2つのパラメーターを取ります。
Array.prototype.reduce( callback, initialItem )
このcallback
関数は次のパラメーターを取ります
(accumulator, itemInArray, indexInArray, entireArray) => { /* do stuff */ }
最初の反復では、
が指定されている場合initialItem
、reduce
関数はをinitialItem
として渡しaccumulator
、配列の最初の項目をとして渡しitemInArray
ます。
initialItem
が指定されていない場合、reduce
関数は配列の最初の項目をとして渡し、配列initialItem
の2番目の項目をitemInArray
混乱させる可能性がある配列として渡します。
私は常に、reduceの初期値を設定することをお勧めします。
次のURLでドキュメントを確認できます。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
お役に立てれば!
指摘されたものを形式化するために、レデューサーは、偶然に同じ型である可能性のある2つの引数を取り、最初の引数と一致する型を返すカタモフィズムです。
function reducer (accumulator: X, currentValue: Y): X { }
つまり、レデューサーの本体は、currentValue
の現在の値をaccumulator
新しいの値に変換する必要がありますaccumulator
。
これは、アキュムレータと要素の値がたまたま同じ型である(ただし、目的が異なる)ため、追加時に簡単に機能します。
[1, 2, 3].reduce((x, y) => x + y);
それらはすべて数字なので、これはうまくいきます。
[{ age: 5 }, { age: 2 }, { age: 8 }]
.reduce((total, thing) => total + thing.age, 0);
次に、アグリゲーターに開始値を与えます。開始値は、ほとんどの場合、アグリゲーターに期待されるタイプ(最終値として出力されると予期されるタイプ)である必要があります。これを強制されることはありませんが(強制してはいけません)、覚えておくことが重要です。
それがわかったら、他のn:1関係の問題に対して意味のある削減を書くことができます。
繰り返される単語を削除する:
const skipIfAlreadyFound = (words, word) => words.includes(word)
? words
: words.concat(word);
const deduplicatedWords = aBunchOfWords.reduce(skipIfAlreadyFound, []);
見つかったすべての単語の数を提供します。
const incrementWordCount = (counts, word) => {
counts[word] = (counts[word] || 0) + 1;
return counts;
};
const wordCounts = words.reduce(incrementWordCount, { });
配列の配列を単一のフラット配列に減らす:
const concat = (a, b) => a.concat(b);
const numbers = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
].reduce(concat, []);
さまざまなことから、1:1に一致しない単一の値に変更する場合は常に、reduceを検討する必要があります。
実際、マップとフィルターはどちらもリダクションとして実装できます。
const map = (transform, array) =>
array.reduce((list, el) => list.concat(transform(el)), []);
const filter = (predicate, array) => array.reduce(
(list, el) => predicate(el) ? list.concat(el) : list,
[]
);
これが、使用方法に関するさらなるコンテキストを提供することを願っていますreduce
。
まだ追加していませんが、これに1つ追加したのは、配列要素が関数であるため、入力と出力の型が動的であることを特に想定している場合です。
const compose = (...fns) => x =>
fns.reduceRight((x, f) => f(x), x);
const hgfx = h(g(f(x)));
const hgf = compose(h, g, f);
const hgfy = hgf(y);
const hgfz = hgf(z);
最初の反復では、「a」は配列の最初のオブジェクトになるため、ax + bxは1 + 2、つまり3を返します。次の反復では、返された3がaに割り当てられるため、aはaxを呼び出すnの数になりますNaNを与えます。
単純な解決策は、まず配列内の数値をマッピングし、次にそれらを以下のように減らすことです。
arr.map(a=>a.x).reduce(function(a,b){return a+b})
ここでarr.map(a=>a.x)
は、数値の配列[1,2,4]を提供します。使用.reduce(function(a,b){return a+b})
すると、これらの数値を簡単に追加できます。
別の簡単な解決策は、以下のように 'a'に0を割り当てることにより、初期合計をゼロとして提供することです。
arr.reduce(function(a,b){return a + b.x},0)
最初のステップでは、の値はa
1であり、の値は2であるため正常に機能しますb
が、2 + 1が返され、次のステップではの値がb
戻り値from step 1 i.e 3
になるためb.x
、未定義になります。 .and undefined + anyNumberはNaNになるため、その結果が得られます。
arr.reduce(function(a,b){return a + b.x},0);
オブジェクトの配列のように、大量のデータを含む複雑なオブジェクトがある場合、これを解決するために段階的なアプローチを取ることができます。
たとえば:
const myArray = [{ id: 1, value: 10}, { id: 2, value: 20}];
まず、配列を目的の新しい配列にマップする必要があります。この例では、値の新しい配列である可能性があります。
const values = myArray.map(obj => obj.value);
このコールバック関数は、元の配列の値のみを含む新しい配列を返し、値constに格納します。これで、値constは次のような配列になります。
values = [10, 20];
これで、削減を実行する準備ができました。
const sum = values.reduce((accumulator, currentValue) => { return accumulator + currentValue; } , 0);
ご覧のとおり、reduceメソッドはコールバック関数を複数回実行します。毎回、配列内のアイテムの現在の値を取り、アキュムレータと合計します。したがって、適切に合計するには、reduceメソッドの2番目の引数としてアキュムレータの初期値を設定する必要があります。
これで、値が30の新しいconst sumが得られました。
縮小関数はコレクションを反復します
arr = [{x:1},{x:2},{x:4}] // is a collection
arr.reduce(function(a,b){return a.x + b.x})
に変換します:
arr.reduce(
//for each index in the collection, this callback function is called
function (
a, //a = accumulator ,during each callback , value of accumulator is
passed inside the variable "a"
b, //currentValue , for ex currentValue is {x:1} in 1st callback
currentIndex,
array
) {
return a.x + b.x;
},
accumulator // this is returned at the end of arr.reduce call
//accumulator = returned value i.e return a.x + b.x in each callback.
);
各インデックスコールバック中に、変数「アキュムレータ」の値がコールバック関数の「a」パラメータに渡されます。「アキュムレータ」を初期化しない場合、その値は未定義になります。undefined.xを呼び出すと、エラーが発生します。
これを解決するには、上に示したCaseyの回答のように、「アキュムレータ」を値0で初期化します。
「reduce」関数の入出力を理解するには、この関数のソースコードを確認することをお勧めします。Lodashライブラリには、ES6の「reduce」関数とまったく同じように機能するreduce関数があります。
ここにリンクがあります: ソースコードを減らします
配列削減関数は、3つのパラメーター、つまり、initialValue(デフォルトは0)、アキュムレーター、および現在の値を取ります。デフォルトでは、initialValueの値は "0"になります。これはアキュムレータによって取得されます
これをコードで見てみましょう。
var arr =[1,2,4] ;
arr.reduce((acc,currVal) => acc + currVal ) ;
// (remember Initialvalue is 0 by default )
//first iteration** : 0 +1 => Now accumulator =1;
//second iteration** : 1 +2 => Now accumulator =3;
//third iteration** : 3 + 4 => Now accumulator = 7;
No more array properties now the loop breaks .
// solution = 7
ここで、初期値を持つ同じ例:
var initialValue = 10;
var arr =[1,2,4] ;
arr.reduce((acc,currVal) => acc + currVal,initialValue ) ;
/
// (remember Initialvalue is 0 by default but now it's 10 )
//first iteration** : 10 +1 => Now accumulator =11;
//second iteration** : 11 +2 => Now accumulator =13;
//third iteration** : 13 + 4 => Now accumulator = 17;
No more array properties now the loop breaks .
//solution=17
同じことがオブジェクト配列にも適用されます(現在のスタックオーバーフローの質問):
var arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(acc,currVal){return acc + currVal.x})
// destructing {x:1} = currVal;
Now currVal is object which have all the object properties .So now
currVal.x=>1
//first iteration** : 0 +1 => Now accumulator =1;
//second iteration** : 1 +2 => Now accumulator =3;
//third iteration** : 3 + 4 => Now accumulator = 7;
No more array properties now the loop breaks
//solution=7
心に留めておくべきことの1つは、デフォルトでInitialValueであり、0であり、{}、[]および数値を意味するものは何でも指定できます。
arr.reduce(function(a,b){return a + b})
が2番目の例で意味すると思います。