pivot - grid
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>klass</title> <script src="./pivot.js"></script> </head> <body> <script type="text/javascript"> var data = [ // 姓名、学科、老师、成绩级别、成绩、个人平均分、班级 {a:'张三', b: '语文', c: '陈中国', d: '优', e: 90, f: 86, g: '一班'}, {a:'李四', b: '语文', c: '郑明华', d: '良', e: 85, f: 72, g: '二班'}, {a:'王五', b: '语文', c: '陈中国', d: '中', e: 62, f: 66, g: '三班'}, {a:'赵六', b: '语文', c: '陈中国', d: '差', e: 50, f: 63, g: '四班'}, {a:'李四', b: '数学', c: '刘小辉', d: '优', e: 93, f: 86, g: '三班'}, {a:'李四', b: '英语', c: 'MissLi', d: '良', e: 80, f: 86, g: '三班'}, {a:'李四', b: '政治', c: '郑明华', d: '良', e: 82, f: 86, g: '三班'} ]; var column = [ { dataIndex: 'b' }, { dataIndex: 'g' }, { dataIndex: 'c'} ]; var row = [ { dataIndex: 'a' },{ dataIndex: 'b' },{ dataIndex: 'c'} ]; var value = [ { dataIndex: 'd', text: '成绩级别' }, { dataIndex: 'e', text: '成绩' }, { dataIndex: 'f', text: '个人平均分' } ]; var ret = pivot(data, { column, row, value }); print(ret); </script> </body> </html>
function pivot(data, { column = [], row = [], value = [] }) { var colKeys = values(column, 'dataIndex'); var rowKeys = values(row, 'dataIndex'); var columnTreeMap = nest(data, colKeys); var rowTreeMap = nest(data, rowKeys); var refs = getNodeRefs(rowTreeMap); var headerNodes = getNodeByDataIndex(columnTreeMap, colKeys.slice(-1)[0]); var valueKeys = values(value, 'dataIndex'); var result = toList(refs, headerNodes, valueKeys); return addColumnHeaderAndRowHeader(result, columnTreeMap, headerNodes, column, rowTreeMap, row, value); } function columnHeaderGroups(columnTreeMap, row, index = 0, groups = []) { var len = row.length; if (!groups[index]) { groups[index] = []; while(len--) groups[index].push(null); } columnTreeMap.forEach((tm, key) => { let nodes = tm.get('nodes') - 1; groups[index].push(key); while(nodes--) { groups[index].push(null); } if (tm.get('children').size) { columnHeaderGroups(tm.get('children'), row, index + 1, groups); } }); return groups; } function getRowHeader(rowTreeMap, row, index = 0, rowHeaders = []) { rowTreeMap.forEach((tm, key) => { let ret = row.map(r => { return r.dataIndex === tm.get('dataIndex') ? key : null; }); rowHeaders.push(ret); if (tm.get('children').size) { getRowHeader(tm.get('children'), row, index + 1, rowHeaders); } }); return rowHeaders; } function countLeafNodes(treeMap, valueSize = 1) { var nodes = 0; treeMap.forEach(tm => { var count; if (tm.get('children').size) { count = countLeafNodes(tm.get('children'), valueSize); } else { count = 1; } tm.set('nodes', count * valueSize); nodes += count; }); return nodes; } function addColumnHeaderAndRowHeader(data, columnTreeMap, headerNodes, column, rowTreeMap, row, value) { var rowHeaders = getRowHeader(rowTreeMap, row); data = data.map((d, i) => rowHeaders[i].concat(d)); var colTexts = values(value, 'text'); var valueHeaders = headerNodes.reduce((header, tm) => header.concat(colTexts), []); countLeafNodes(columnTreeMap, value.length); var groups = columnHeaderGroups(columnTreeMap, row); var blanks = row.map(r => null); data.unshift(blanks.concat(valueHeaders)); data = groups.concat(data); return data; } function nest(data, keys, index = 0, treeMap = new Map()) { if (keys.length === index) { return treeMap; } var key = keys[index]; data.forEach(d => { if (treeMap.has(d[key])) { treeMap.get(d[key]).get('data').push(d); } else { let obj = new Map() .set('key', d[key]) .set('dataIndex', key) .set('data', [d]) .set('ref', record => record[key] === d[key]) .set('children', new Map()); treeMap.set(d[key], obj); } }); treeMap.forEach(related => { nest(related.get('data'), keys, index + 1, related.get('children')); }); return treeMap; } function getNodeRefs(treeMap, refs = [], parentRef) { treeMap.forEach(tm => { let fn = tm.get('ref'); if (parentRef) { refs.push(record => { return parentRef(record) && fn(record); }); } else { refs.push(fn); } if (tm.get('children').size) { getNodeRefs(tm.get('children'), refs, fn); } }); return refs; } function getNodeByDataIndex(treeMap, dataIndex, nodes = []) { if (!dataIndex) return nodes; treeMap.forEach(tm => { if (tm.get('dataIndex') === dataIndex) { nodes.push(tm); } if (tm.get('children').size) { getNodeByDataIndex(tm.get('children'), dataIndex, nodes); } }); return nodes; } function values(collection, key) { return collection.map(obj => obj[key]); } function getValue (items, ref, vk) { for (var i = 0; i < items.length; i++) { if (ref(items[i])) { return items[i][vk]; } } return null; } function toList(refs, headerNodes, valueKeys) { var result = []; refs.forEach((ref, i) => { result[i] = []; headerNodes.forEach((tm, j) => { valueKeys.forEach(vk => { result[i].push(getValue(tm.get('data'), ref, vk)); }); }); }); return result; } function print(rows) { var tpl = '<table border=1>'; rows.forEach(row => { tpl += '<tr>'; row.forEach(v => { tpl += '<td>' + (v || '-') + '</td>'; }); tpl += '</tr>'; }); tpl += '</table>'; document.write(tpl); }