/** * PivotGrid 집계 함수 유틸리티 * 다양한 집계 연산을 수행합니다. */ import { AggregationType, PivotFieldFormat } from "../types"; // ==================== 집계 함수 ==================== /** * 합계 계산 */ export function sum(values: number[]): number { return values.reduce((acc, val) => acc + (val || 0), 0); } /** * 개수 계산 */ export function count(values: any[]): number { return values.length; } /** * 평균 계산 */ export function avg(values: number[]): number { if (values.length === 0) return 0; return sum(values) / values.length; } /** * 최소값 계산 */ export function min(values: number[]): number { if (values.length === 0) return 0; return Math.min(...values.filter((v) => v !== null && v !== undefined)); } /** * 최대값 계산 */ export function max(values: number[]): number { if (values.length === 0) return 0; return Math.max(...values.filter((v) => v !== null && v !== undefined)); } /** * 고유값 개수 계산 */ export function countDistinct(values: any[]): number { return new Set(values.filter((v) => v !== null && v !== undefined)).size; } /** * 집계 타입에 따른 집계 수행 */ export function aggregate( values: any[], type: AggregationType = "sum" ): number { const numericValues = values .map((v) => (typeof v === "number" ? v : parseFloat(v))) .filter((v) => !isNaN(v)); switch (type) { case "sum": return sum(numericValues); case "count": return count(values); case "avg": return avg(numericValues); case "min": return min(numericValues); case "max": return max(numericValues); case "countDistinct": return countDistinct(values); default: return sum(numericValues); } } // ==================== 포맷 함수 ==================== /** * 숫자 포맷팅 */ export function formatNumber( value: number | null | undefined, format?: PivotFieldFormat ): string { if (value === null || value === undefined) return "-"; const { type = "number", precision = 0, thousandSeparator = true, prefix = "", suffix = "", } = format || {}; let formatted: string; switch (type) { case "currency": formatted = value.toLocaleString("ko-KR", { minimumFractionDigits: precision, maximumFractionDigits: precision, }); break; case "percent": formatted = (value * 100).toLocaleString("ko-KR", { minimumFractionDigits: precision, maximumFractionDigits: precision, }); break; case "number": default: if (thousandSeparator) { formatted = value.toLocaleString("ko-KR", { minimumFractionDigits: precision, maximumFractionDigits: precision, }); } else { formatted = value.toFixed(precision); } break; } return `${prefix}${formatted}${suffix}`; } /** * 날짜 포맷팅 */ export function formatDate( value: Date | string | null | undefined, format: string = "YYYY-MM-DD" ): string { if (!value) return "-"; const date = typeof value === "string" ? new Date(value) : value; if (isNaN(date.getTime())) return "-"; const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); const quarter = Math.ceil((date.getMonth() + 1) / 3); return format .replace("YYYY", String(year)) .replace("MM", month) .replace("DD", day) .replace("Q", `Q${quarter}`); } /** * 집계 타입 라벨 반환 */ export function getAggregationLabel(type: AggregationType): string { const labels: Record = { sum: "합계", count: "개수", avg: "평균", min: "최소", max: "최대", countDistinct: "고유값", }; return labels[type] || "합계"; }