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);
}

 

posted @ 2017-10-12 22:07  风之约  阅读(213)  评论(0编辑  收藏  举报