63
46

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

reduceの使い方メモ(主に集計目的)

Last updated at Posted at 2020-01-09

reduceを集計目的で使うときのメモ。

reduceの簡単な説明

mapとかfilterとかと同じ部類の関数ですが、他の関数に比べ(名前的にも)何してるのかわかりにくいですが、

[結果] = array.reduce([callback],初期値);

というような形式をとり(初期値は省略可能ですが、prevの意味を理解するためにも付けてたほうがいいです)、前の実行結果の影響を受ける(値を利用する)のが特徴です。

で、下記のように使います。

const array = [1,2,3];
const result = array.reduce((prev,current)=>prev + current,0);

reduceもmap等とどうようループします。で今回は配列が3つなので3回ループし、それぞれの処理として下記のような処理が実行されます。

  • 1回目:prevの値は初期値の0、currentには1が入ります。なので、0 + 1 = 1が(次のprev値として)返ります。
  • 2回目:prevの値は1、currentには2が入るので、 1 + 2 = 3が返ります。
  • 3回目:prevの値は3、currentには3が入るので、 3 + 3 = 6が返ります。で、これが最終的な結果として返ります。

いかがでしょうか。

基本

プリミティブ

合計、平均値、最大値、最小値とか。
平均値に関してだけは、sumを利用して別途計算するのがてっとり速いかと。

const numbers = [1, 2, 3, 4, 5];

//合計
const sum = numbers.reduce((prev, current) => prev + current, 10);
console.log(sum);

//平均値
const average = sum / numbers.length;
console.log(average);

//最大値
const max = numbers.reduce((prev, current) => prev > current ? prev : current);
console.log(max);

//最小値
const min = numbers.reduce((prev, current) => prev < current ? prev : current);
console.log(min);


25
5
5
1

オブジェクト

次にオブジェクト。実際の現場ではこちらのほうが一般的かと。
ま、対象とするkeyを指定するだけで、基本的にはプリミティブと同じ。
とりあえず、合計と最大値のサンプル(後は割愛)。

//data
const members = [
    { name: "foo", score: 10, dep: 'sales1' },
    { name: "bar", score: 20, dep: 'sales2' },
    { name: "baz", score: 30, dep: 'sales2' },
]

//合計
const scoreSum = members.reduce((prev, current) => {
    return prev.score + current.score;
}, 0);
console.log(scoreSum);

//最大値
const maxScore = members.reduce((prev, current) => {
    return prev.score > current.score ? prev.score : current.score;
}, 0);
console.log(maxScore);


60
30

応用

GroupBy的な

実開発ではよく使うのでメモ。
出力するデータ・フォーマットをイメージしておく必要があります。今回は、部署(dep)毎に下記のような形式でデータを出力する想定。

{
	dep:DEP_NAME, //部署名
	count COUNT, //何件あるか
	score: SCORE //スコアの合計
}

では作ってみます。

//souce data
const members = [
    { name: "foo", score: 10, dep: 'sales1' },
    { name: "bar", score: 20, dep: 'sales2' },
    { name: "baz", score: 30, dep: 'sales2' },
]

//groupBy
const groupBy = members.reduce((result, current) => {
	
	//部署がprevにあるか
    const element = result.find(value => value.dep === current.dep);

    if (element) {
    	//ある時(下記、初期データを操作)
        element.count++;
        element.score += current.score;
    } else {
    	//無いとき(新規に初期データを作成)
        result.push({
            dep: current.dep,
            count: 1,
            score: current.score,
        })
    }
    return result;
    
}, []); //初期値は[]

console.log(groupBy);



[
  { dep: 'sales1', count: 1, score: 10 },
  { dep: 'sales2', count: 2, score: 50 }
]

Average

平均を追記してみます。出力されたデータをmapでループして平均値を追加しています。

const withAverage = groupBy.map(item => ({ ...item, ave: item.score / item.count }));
console.log(withAverage);

[
  { dep: 'sales1', count: 1, score: 10, ave: 10 },
  { dep: 'sales2', count: 2, score: 50, ave: 25 }
]

平均には合計と合計カウントが必要なので1ループでは無理ですかね。。。mapをつなげて書くことはできるかと思います。

Sort

ソート。まあ基本の応用。

//sort
const sorted = withAverage.sort((prev, current) => prev.score < current.score ? 1 : -1); //大きい順
// const sorted = groupBy.sort((prev, current) => prev.score > current.score ? 1 : -1); //小さい順
console.log(sorted);


[
  { dep: 'sales2', count: 2, score: 50, ave: 25 },
  { dep: 'sales1', count: 1, score: 10, ave: 10 }
]

ひとまず以上です。必要に応じて随時追加予定。

番外編:配列データのJOIN(client side join)

集計の際によく2つの配列をJOINすることがあるのでついでに書いておきます。

const deps = [
    { depId: 1, depName: "経理" },
    { depId: 2, depName: "営業" },
    { depId: 3, depName: "その他" },
];

const members = [
    { name: "foo", score: 10, depId: 1 },
    { name: "bar", score: 20, depId: 2 },
    { name: "baz", score: 30, depId: 3 },
]

const membersWithDep = members.map(member => {
	//既存のmemberの要素を展開しつつ、depIdがマッチしたdepNameの値を追記する
    return { ...member, depName: deps.find(value => value.depId === member.depId).depName }
});

console.log(membersWithDep);


[
  { name: 'foo', score: 10, depId: 1, depName: '経理' },
  { name: 'bar', score: 20, depId: 2, depName: '営業' },
  { name: 'baz', score: 30, depId: 3, depName: 'その他' }
]

その他

Firestoreでの適用事例?を記述しました。

63
46
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
63
46

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?