blog

配列のreduceメソッドに関するシナリオと質問。

これでほとんどの問題は解決し、初心者にreduceの書き方のヒントを与えることができます。 以下は、アプリケーションのシナリオとグッドプラクティスの例の両方です。 より読みやすくしたいのであれば、in...

May 30, 2020 · 6 min. read
シェア

reduceが行う最大のことは、配列から値を取得し、配列の各要素に対してreducer関数を実行することです。

は同等のものを開きます:

// 理解しやすくするために、以下のように追加する。initValue
[x1, x2, x3].reduce(f,initValue) 
// f少なくとも2つのパラメータ 
f(f(f(initValue,x1), x2), x3);

REDUCEを書く核心は実ははっきりしています:

  • initValue
  • fは常に2つの引数accとitemを持っています。

これでほとんどの問題は解決し、初心者はREDUCEの書き方がいかに簡単かを知ることができるでしょう。

以下は、応用シナリオと良い練習の例です。

配列のすべての値を累積

const sumFn = (acc, item) => acc + item;
const sum = [1, 2, 3, 4].reduce(sumFn, 0);
// 6
console.log(sum);

オブジェクト配列の値を累積

const sumFn = (acc, item) => acc + item.x;
const sum = [{ x: 1 }, { x: 2 }, { x: 3 }, { x: 4 }].reduce(sumFn, 0);
// 6
console.log(sum);

低次元配列、2Dから1Dへ

// pushの代わりにconcatを使う理由は、concatはマージされた配列を返すのに対し、pushは配列の長さを返すからだ。
const flat = (acc, item) => acc.concat(item);
const res = [
 [0, 1],
 [2, 3],
 [4, 5]
].reduce(flat, []);
// [0, 1, 2, 3, 4, 5]
console.log(res);
const flat = (acc, item) => acc.concat(item);
const res = [ 0, 1, [2, 3, [4, 5]] ].reduce(flat, []);
// [0, 1, 2, 3, 4, 5]
console.log(res)
// ここにアップデートがある。少し抽象化された関数で、現在のアイテムが配列であればフラット化し、そうでなければ連結する。
const flatten = arr =>
 arr.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []);
const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];
const getCount = (obj, key) => {
 key in obj || (obj[key] = 0);
 obj[key]++;
 return obj;
};
const res = names.reduce(getCount, {});
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
console.log(res);
const people = [
 { id: 1, name: "Alice", age: 21 },
 { id: 2, name: "Max", age: 20 },
 { id: 3, name: "Jane", age: 20 }
];
const getType = (obj, item) => {
 const key = item.age;
 key in obj || (obj[key] = []);
 obj[key].push(item);
 return obj;
};
const res = people.reduce(getType, {});
// { 20: [ { name: 'Max', age: 20 }, { name: 'Jane', age: 20 } ], 21: [{ name: 'Alice', age: 21 }] }
console.log(res);

任意のフィールドを分類するために、ここで少しアップグレードすることができます。

const classifyArray = (arr, keyClassified) => {
 const getType = (obj, item) => {
 const key = item[keyClassified];
 key in obj || (obj[key] = []);
 obj[key].push(item);
 return obj;
 };
 return arr.reduce(getType, {});
};
const people = [
 { id: 1, name: "Alice", age: 21, gender: 0 },
 { id: 2, name: "Max", age: 20, gender: 1 },
 { id: 3, name: "Jane", age: 20, gender: 0 }
];
// { '0': [ { id: 1, name: 'Alice', age: 21, gender: 0 }, { id: 3, name: 'Jane', age: 20, gender: 0 } ], '1': [ { id: 2, name: 'Max', age: 20, gender: 1 } ] }
console.log(classifyArray(people, "gender"));

拡張演算子によるオブジェクト配列の配列処理

// すべての本をマージする
var friends = [
 {
 name: "Anna",
 books: ["Bible", "Harry Potter"],
 age: 21
 },
 {
 name: "Bob",
 books: ["War and peace", "Romeo and Juliet"],
 age: 26
 },
 {
 name: "Alice",
 books: ["The Lord of the Rings", "The Shining"],
 age: 18
 }
];
const sumArr = (oldArr, item) => {
 return [...oldArr, ...item.books];
};
const res = friends.reduce(sumArr, []);
// [ 'Bible', 'Harry Potter', 'War and peace', 'Romeo and Juliet', 'The Lord of the Rings', 'The Shining' ]
console.log(res);
var myArray = ["a", "b", "a", "b", "c", "e", "e", "c", "d", "d", "d", "d"];
const addArr = (acc, item) => (acc.includes(item) ? acc : [...acc, item]);
const res = myArray.reduce(addArr, []);
// [ 'a', 'b', 'c', 'e', 'd' ]
console.log(res);

もちろん、もっと簡単な方法はあります。

const unique = arr => Array.from(new Set(arr));
// [ 'a', 'b', 'c', 'e', 'd' ]
console.log(
 unique(["a", "b", "a", "b", "c", "e", "e", "c", "d", "d", "d", "d"])
);
// promise function 1
function p1(a) {
 return new Promise(resolve => {
 resolve(a * 5);
 });
}
// MapReduceだ!これは普通の機能だよ!
function f(a) {
 return a * 2;
}
// promise function 2
function p2(a) {
 return new Promise(resolve => {
 resolve(a - 2);
 });
}
const arr = [p1, f, p2];
const runOrderly = (acc, item) => acc.then(item);
const res = arr.reduce(runOrderly, Promise.resolve(10));
// 98
console.log(res);

汎用の逐次実行関数をもう一度抽象的に書いてください。

const runOrderly = (arr, initValue) =>
 arr.reduce((acc, item) => acc.then(item), Promise.resolve(initValue));

例として、組み合わせ関数とは何かを見てみましょう。

const sum = (a, b) => a + b;
const len = str => str.length;
const addCurrency = str => "$" + str;
// 今、私はまず2つの文字の和を求め、次に長さを求め、次に$これは新しい関数で、既知の関数を組み合わせたものである。
const newFn = (a, b) => addCurrency(len(sum(a, b)));
console.log(newFn("xyz", "abc"));

compose(addCurrency,len,sum)しかし、上記のようにnewFnを地獄のようにネストして生成する代わりに、できれば、ここではreduceRightを試すことができます。

// compose(addCurrency,len,sum)
const compose = (...fns) => {
 return (...args) => {
 // ここで、最後の関数は他の関数と違ってパラメータが2つあるので、他の関数がすべてfn(acc)
 const lastFn = fns.pop();
 let initValue = lastFn(...args);
 const f = (acc, fn) => fn(acc);
 return fns.reduceRight(f, initValue);
 };
};
const newFn = compose(addCurrency, len, sum);
console.log(newFn("xyz", "abc"));
// 簡略化する
const compose = (...fns) => (...args) =>
 fns.reduceRight((acc, fn) => fn(acc), fns.pop()(...args));

もちろん、思考がもう少しパワフルであれば、REDUCEを使うこともできます。

// compose(addCurrency,len,sum)
const compose = (...fns) =>
 fns.reduce((acc, cur) => (...args) => acc(cur(...args)));

これは考えにくい方法ですが、lenとsumの2つの関数だけを組み合わせたとします。

  • compose (...args) => len(sum(...args))
  • リデュースに相当するのは(...args)=>[len,sum].reduce((acc,curFn)=>(...args) => acc(curFn(...args)))
  • len,sum]を他の配列に置き換えてください。
  • 今度自分で書くかどうかはわからないけど、たぶんReduceRightにすると思います。

リデュースの落とし穴

  • 空の配列は reduce
  • reduce(f) に initValue がない場合、f の最初の引数は arr[0] arr[1] 1 arr
  • fには少なくとも2つの引数が必要です。arr[0] arr[1] 1 arr

より読みやすくするために、initValueを渡すことで、各操作に一貫性を持たせ、エラーを少なくすることができます。この記事でも

引用

Read next

デイリーダイジェスト - ランダムな名前や携帯電話番号の章を生成する

IDカード/銀行カード/メールボックス/ニックネームなどを記入するフォローアップ:時にはいくつかのビジネスシナリオでは、表示するためのいくつかの仮想データを生成する必要がありますもちろん、偽であると言うことはできません、この時点で、次のコードなどのいくつかのツールが必要になります。

May 30, 2020 · 3 min read