0
0

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.

[memo] JavaScript/TypeScriptで配列をreduceを使って連想配列に畳み込む

Last updated at Posted at 2023-02-21

はじめに

  • JavaScript/TypeScript で Arrayreduce() を使って連想配列(JavaScriptの普通のオブジェクト)に畳み込む方法がある
  • 同様に Map オブジェクトに畳み込む方法もある
  • そのうち忘れるはずなので、その時のためにメモを残しておく

何も考えず forEach で実装する

配列を処理/加工して、ハッシュマップや連想配列みたいなものにしたいことってあると思います。

前提として以下のような型やデータがあるとします。

interface Info {
    name: string;
    id: string;
    // ...
}

type InfoMap = { [id: string]: Info };

const infos: Info[] = [
    { id: "1", name: "foo", /* ... */ },
    { id: "2", name: "bar", /* ... */ },
    { id: "3", name: "baz", /* ... */ },
];

ここで、infos をもとに InfoMap を作りたいとします。
何も考えないで実装すると、以下のようになると思います。

const infoMap: InfoMap = {};
infos.forEach((info) => {
    infoMap[info.id] = info;
});

これでもいいんですけど、宣言的な感じじゃなくて、ちょっともやっとしますよね。

reduce で実装

JavaScript の Array には reduce() という、畳み込み用のメソッドがあります。

配列の合計を求めるとか、最大値を求めるとかに使ったりすると思います。

const data = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
const sum = data.reduce((a, b) => a + b, 0);
const maxData = data.reduce((a, b) => Math.max(a, b), Number.NEGATIVE_INFINITY);

上記の場合、配列要素と、畳み込み結果(および初期値)の型は同じ( number )になっていますが、実は違っていても大丈夫らしいです。

なので、以下のようにして連想配列に畳み込むことができます。

const infoMap = infos.reduce<InfoMap>((map, info) => {
    map[info.id] = info;
    return map;
}, {});

ポイントは return map; が必要なあたりでしょうか。

ちなみにジェネリック型を明示しないで、以下のように初期値を {} as InfoMap とすることもできますが、やっぱり as を使うよりは型安全な上記の方が良いのかな(初期値は空オブジェクトなのであまり変わらないかもしれないけど)。

as を使う方法
const infoMap = infos.reduce((map, info) => {
    map[info.id] = info;
    return map;
}, {} as InfoMap);

最近の JavaScript には Map オブジェクトというものもあるので、Map オブジェクトに畳み込む例もメモしておきます。

as を使う方法
const infoMap = infos.reduce(
    (map, info) => map.set(info.id, info),
    new Map<string, Info>()
);

Map オブジェクトの場合、set() メソッドは、メソッドチェーンのために Map オブジェクトそのものを返すので、return map; みたいな処理を書かなくてよいので少しすっきりしていますね。

ただし、単純な set() 以外のことがやりたい場合には、return map; が必要になるので、注意が必要です。

もっと簡単な方法(追記)

単に、連想配列、Map オブジェクトにするだけであればもっと簡単な方法があります。

連想配列にする場合

const infoMap = Object.fromEntries(infos.map(x => [x.id, x]));

Map オブジェクトにする場合

const infoMap = new Map(infos.map(x => [x.id, x]));

ただ、もっと複雑な集計等をしたい(例:配列には月ごとのデータがあって、連想配列には同一キーごとに期間内の合計を求めたい)場合には reduce() を使う必要があると思います。
(本記事は、そのあたりの説明が抜けているので注意してください。あとで見直すかも)

参考

0
0
2

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?