UNPKG

data-utils-grok

Version:

A comprehensive JavaScript utility library for data processing, organized by data types (arrays, objects, strings, forms, tables)

372 lines (331 loc) 10.8 kB
// src/tableUtils.js /** * 表格类型处理工具函数 */ /** * 表格数据排序 * @param {Array} data - 表格数据 * @param {string} key - 排序字段 * @param {string} [order='asc'] - 排序方向 'asc' | 'desc' * @returns {Array} 排序后的数据 */ function sortTableData(data, key, order = 'asc') { if (!Array.isArray(data)) { throw new TypeError('Data must be an array'); } if (typeof key !== 'string') { throw new TypeError('Key must be a string'); } if (!['asc', 'desc'].includes(order)) { throw new TypeError('Order must be "asc" or "desc"'); } return [...data].sort((a, b) => { const valueA = a[key]; const valueB = b[key]; if (valueA === valueB) return 0; if (valueA === null || valueA === undefined) return 1; if (valueB === null || valueB === undefined) return -1; const comparison = valueA < valueB ? -1 : 1; return order === 'asc' ? comparison : -comparison; }); } /** * 表格数据过滤 * @param {Array} data - 表格数据 * @param {Object} filters - 过滤条件 * @returns {Array} 过滤后的数据 */ function filterTableData(data, filters) { if (!Array.isArray(data)) { throw new TypeError('Data must be an array'); } if (!filters || typeof filters !== 'object') { return data; } return data.filter(item => { for (const [key, filterValue] of Object.entries(filters)) { if (filterValue === null || filterValue === undefined || filterValue === '') { continue; } const itemValue = item[key]; if (typeof filterValue === 'string') { if (!itemValue || !itemValue.toString().toLowerCase().includes(filterValue.toLowerCase())) { return false; } } else if (typeof filterValue === 'number') { if (itemValue !== filterValue) { return false; } } else if (Array.isArray(filterValue)) { if (!filterValue.includes(itemValue)) { return false; } } else if (typeof filterValue === 'function') { if (!filterValue(itemValue, item)) { return false; } } } return true; }); } /** * 表格数据分页 * @param {Array} data - 表格数据 * @param {number} page - 当前页码(从1开始) * @param {number} pageSize - 每页大小 * @returns {Object} 分页结果 { data, total, page, pageSize, totalPages } */ function paginateTableData(data, page, pageSize) { if (!Array.isArray(data)) { throw new TypeError('Data must be an array'); } if (typeof page !== 'number' || page < 1) { throw new TypeError('Page must be a positive number'); } if (typeof pageSize !== 'number' || pageSize < 1) { throw new TypeError('Page size must be a positive number'); } const total = data.length; const totalPages = Math.ceil(total / pageSize); const startIndex = (page - 1) * pageSize; const endIndex = startIndex + pageSize; const paginatedData = data.slice(startIndex, endIndex); return { data: paginatedData, total, page, pageSize, totalPages, hasNext: page < totalPages, hasPrev: page > 1 }; } /** * 表格数据分组 * @param {Array} data - 表格数据 * @param {string|Function} groupBy - 分组字段或函数 * @returns {Object} 分组后的数据 */ function groupTableData(data, groupBy) { if (!Array.isArray(data)) { throw new TypeError('Data must be an array'); } return data.reduce((groups, item) => { const key = typeof groupBy === 'function' ? groupBy(item) : item[groupBy]; if (!groups[key]) { groups[key] = []; } groups[key].push(item); return groups; }, {}); } /** * 表格数据聚合 * @param {Array} data - 表格数据 * @param {string} groupBy - 分组字段 * @param {Object} aggregations - 聚合配置 * @returns {Array} 聚合后的数据 */ function aggregateTableData(data, groupBy, aggregations) { if (!Array.isArray(data)) { throw new TypeError('Data must be an array'); } if (typeof groupBy !== 'string') { throw new TypeError('Group by must be a string'); } if (!aggregations || typeof aggregations !== 'object') { throw new TypeError('Aggregations must be an object'); } const grouped = groupTableData(data, groupBy); const result = []; for (const [key, group] of Object.entries(grouped)) { const aggregated = { [groupBy]: key }; for (const [field, config] of Object.entries(aggregations)) { const { type, sourceField } = config; const values = group.map(item => item[sourceField]).filter(val => val !== null && val !== undefined); switch (type) { case 'sum': aggregated[field] = values.reduce((sum, val) => sum + Number(val), 0); break; case 'average': aggregated[field] = values.length > 0 ? values.reduce((sum, val) => sum + Number(val), 0) / values.length : 0; break; case 'count': aggregated[field] = values.length; break; case 'min': aggregated[field] = values.length > 0 ? Math.min(...values) : null; break; case 'max': aggregated[field] = values.length > 0 ? Math.max(...values) : null; break; case 'unique': aggregated[field] = [...new Set(values)]; break; } } result.push(aggregated); } return result; } /** * 表格数据导出为CSV * @param {Array} data - 表格数据 * @param {Array} columns - 列配置 * @returns {string} CSV字符串 */ function exportTableToCSV(data, columns) { if (!Array.isArray(data)) { throw new TypeError('Data must be an array'); } if (!Array.isArray(columns)) { throw new TypeError('Columns must be an array'); } const headers = columns.map(col => col.title || col.key).join(','); const rows = data.map(item => { return columns.map(col => { const value = item[col.key]; // 如果值包含逗号、引号或换行符,需要用引号包围 if (typeof value === 'string' && (value.includes(',') || value.includes('"') || value.includes('\n'))) { return `"${value.replace(/"/g, '""')}"`; } return value || ''; }).join(','); }); return [headers, ...rows].join('\n'); } /** * 表格数据导出为JSON * @param {Array} data - 表格数据 * @param {Array} columns - 列配置 * @returns {string} JSON字符串 */ function exportTableToJSON(data, columns) { if (!Array.isArray(data)) { throw new TypeError('Data must be an array'); } if (!Array.isArray(columns)) { throw new TypeError('Columns must be an array'); } const filteredData = data.map(item => { const filtered = {}; columns.forEach(col => { filtered[col.key] = item[col.key]; }); return filtered; }); return JSON.stringify(filteredData, null, 2); } /** * 表格数据去重 * @param {Array} data - 表格数据 * @param {string|Array} keys - 去重字段 * @returns {Array} 去重后的数据 */ function deduplicateTableData(data, keys) { if (!Array.isArray(data)) { throw new TypeError('Data must be an array'); } const keyArray = Array.isArray(keys) ? keys : [keys]; const seen = new Set(); return data.filter(item => { const key = keyArray.map(k => item[k]).join('|'); if (seen.has(key)) { return false; } seen.add(key); return true; }); } /** * 表格数据转换 * @param {Array} data - 表格数据 * @param {Object} transformations - 转换配置 * @returns {Array} 转换后的数据 */ function transformTableData(data, transformations) { if (!Array.isArray(data)) { throw new TypeError('Data must be an array'); } if (!transformations || typeof transformations !== 'object') { return data; } return data.map(item => { const transformed = { ...item }; for (const [field, config] of Object.entries(transformations)) { const { type, sourceField, options = {} } = config; const value = sourceField ? item[sourceField] : item[field]; switch (type) { case 'format': if (options.format === 'currency') { transformed[field] = new Intl.NumberFormat('zh-CN', { style: 'currency', currency: options.currency || 'CNY' }).format(value); } else if (options.format === 'date') { transformed[field] = new Date(value).toLocaleDateString('zh-CN'); } else if (options.format === 'datetime') { transformed[field] = new Date(value).toLocaleString('zh-CN'); } break; case 'map': transformed[field] = options.mapping[value] || value; break; case 'calculate': if (options.operation === 'percentage') { transformed[field] = `${(value * 100).toFixed(options.decimals || 2)}%`; } break; } } return transformed; }); } /** * 表格数据统计 * @param {Array} data - 表格数据 * @param {Array} columns - 列配置 * @returns {Object} 统计信息 */ function getTableStats(data, columns) { if (!Array.isArray(data)) { throw new TypeError('Data must be an array'); } if (!Array.isArray(columns)) { throw new TypeError('Columns must be an array'); } const stats = { totalRows: data.length, totalColumns: columns.length, columnStats: {} }; columns.forEach(col => { const values = data.map(item => item[col.key]).filter(val => val !== null && val !== undefined); if (values.length > 0) { const numericValues = values.filter(val => !isNaN(Number(val))).map(Number); stats.columnStats[col.key] = { count: values.length, uniqueCount: new Set(values).size, hasNulls: values.length < data.length, isNumeric: numericValues.length === values.length, min: numericValues.length > 0 ? Math.min(...numericValues) : null, max: numericValues.length > 0 ? Math.max(...numericValues) : null, average: numericValues.length > 0 ? numericValues.reduce((sum, val) => sum + val, 0) / numericValues.length : null }; } }); return stats; } module.exports = { sortTableData, filterTableData, paginateTableData, groupTableData, aggregateTableData, exportTableToCSV, exportTableToJSON, deduplicateTableData, transformTableData, getTableStats, };