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
JavaScript
// 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,
};