JavaScriptでオブジェクトの配列を効率的にグループ化する方法
以下のようなオブジェクトの配列があるとします。
const arr = [
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
];ここで必要になるのは、このような配列を第一引数として受け取るJavaScript関数を作成することです。
この関数の目的は、第二引数以降で指定された1つまたは複数のプロパティに基づいて、与えられた配列をグループ化し、Valueの合計値を集計できるようにすることです。
たとえば「Phase」だけを基準にグループ化した場合、期待される結果は次のとおりです。
const output = [
{ Phase: "Phase 1", Value: 50 },
{ Phase: "Phase 2", Value: 130 }
];一方、「Phase」と「Step」の2つのプロパティを組み合わせてグループ化した場合は、次の結果が得られます。
const output = [
{ Phase: "Phase 1", Step: "Step 1", Value: 15 },
{ Phase: "Phase 1", Step: "Step 2", Value: 35 },
{ Phase: "Phase 2", Step: "Step 1", Value: 55 },
{ Phase: "Phase 2", Step: "Step 2", Value: 75 }
];実装例
これを実現するコードは以下のとおりです。
const arr = [
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
];
const groupBy = (array, groups, valueKey) => {
const map = new Map;
groups = [].concat(groups);
return array.reduce((acc, val) => {
groups.reduce((accu, value, ind, { length }) => {
let child;
if (accu.has(val[value])) {
return accu.get(val[value]);
};
if (ind + 1 === length) {
child = Object
.assign(...groups.map(value => ({ [value]: val[value]
})), { [valueKey]: 0 });
acc.push(child);
}
else {
child = new Map;
};
accu.set(val[value], child);
return child;
}, map)[valueKey] += +val[valueKey];
return acc;
}, []);
};
console.log(groupBy(arr, 'Phase', 'Value'));
console.log(groupBy(arr, ['Phase', 'Step'], 'Value'));コードのポイント
この実装が効率的である理由は、主に以下の3点にあります。
まず、Mapを使用して階層的なキーを管理しているため、要素へのアクセスが高速に行えます。次に、reduce()を二重に活用することで、グループ化の基準となるプロパティが1つでも複数でも柔軟に対応できます。そして最後に、集計対象の値は+val[valueKey]によって文字列から数値へ変換され、各グループごとに正しく合計されます。
出力結果
コンソールには次のように表示されます。
[ { Phase: 'Phase 1', Value: 50 }, { Phase: 'Phase 2', Value: 130 } ]
[
{ Phase: 'Phase 1', Step: 'Step 1', Value: 15 },
{ Phase: 'Phase 1', Step: 'Step 2', Value: 35 },
{ Phase: 'Phase 2', Step: 'Step 1', Value: 55 },
{ Phase: 'Phase 2', Step: 'Step 2', Value: 75 }
]
-
【JavaScript】配列内の複数オブジェクト間でメソッドを共有する方法(プロトタイプ活用)
配列に格納された複数のオブジェクト間でメソッドを共有したい場合、JavaScriptのプロトタイプ(prototype)を利用するのが最も効率的です。 通常、オブジェクトごとに関数を個別に定義すると、インスタンスの数だけ同じ処理がメモリ上に複製されてしまいます。しかし、コンストラクター関数のprototypeにメソッドを定義すれば、そこから生成されたすべてのインスタンスが同一のメソッドを共有できるようになり、メモリの節約にもつながります。 以下は、プロトタイプを使って配列内の複数のPersonオブジェクト間でwelcome()メソッドを共有するコード例です。 サンプルコード <!DOCT
-
JavaScriptのオブジェクト配列に配列メソッドを適用する方法
JavaScriptでは、オブジェクトが格納された配列に対しても、通常の配列と同じようにpop()、push()、splice()などの標準的な配列メソッドをそのまま使用できます。オブジェクト配列はあくまで「配列」であるため、要素としてオブジェクトが入っていても配列操作のAPIは共通で動作します。 コード例 以下は、JavaScriptオブジェクトの配列に対して配列メソッドを使用するサンプルコードです。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8