Skip to content

Instantly share code, notes, and snippets.

@JeffreyZhao
Last active July 17, 2019 10:47
Show Gist options
  • Save JeffreyZhao/d131293c3f7db2e552c6 to your computer and use it in GitHub Desktop.
Save JeffreyZhao/d131293c3f7db2e552c6 to your computer and use it in GitHub Desktop.

打印表头

小明正在用JavaScript写一个日志分析程序。该程序会将日志转化为CSV文件,以便在Excel等应用中加载为一个表格。现在他在生成表头上遇到了困难。

他需要实现如下一个方法:

function printLine(array) {
    console.log(array.join(","));
}

function printHeader(obj) {
	/* 使用 printLine 输出内容*/
}

输入

小明获取一个对象,需要根据这个对象的结构生成表头。例如:

{
	Process: {
		CpuUsage: 0
		VirtualMemory: 0,
		PrivateMemory: 0
	},
	Subscriptions: {
		Order: {
			TotalCount: 0,
			LastMinute: 0,
			Failed: 0
		},
		User: {
			TotalCount: 0
			LastMinute: 0
		}
	}
}

由于表头仅和对象结构相关,因此无需关注字段的值。

输出

输出为一行至多行文本,为CSV格式,字段之间以逗号隔开(简化问题起见,字段本身不包含逗号)。

如上输入,则需要得到以下输出:

Process,,,Subscriptions
CpuUsage,VirtualMemory,PrivateMemory,Order,,,User
,,,TotalCount,LastMinute,Failed,TotalCount,LastMinute

该CSV文件内容使用Excel打开便会得到以下表格(请忽略格式):

ProcessSubscriptions
CpuUsageVirtualMemoryPrivateMemoryOrderUser
TotalCountLastMinuteFailedTotalCount LastMinute

总而言之,是将对象结构转化为如上表头。

@jetzhliu
Copy link

function printHeader(obj) {
    /* 使用 printLine 输出内容*/
    var out = [];
    function format(obj, level) {
        if (!out[level]) {
            out[level] = [];
        }
        for (var key in obj) {
            out[level][out[out.length-1].length] = key;
            if (typeof obj[key] === 'object') {
                format(obj[key], level+1);
            }
        }
    }
    format(obj, 0);
    out.forEach(printLine);
}

和楼上两位都好像。。。

@ZTGeng
Copy link

ZTGeng commented Mar 22, 2016

又写了个循环的版本:

function printHeader(obj) {
    var result = [], tempStack = [];
    var props = [], objs = [], depths = [];
    var col = 0, lastDepth = -1;
    for (var prop in obj) {
        tempStack.push(prop);
    }
    while (tempStack.length > 0) {
        var prop = tempStack.pop();
        props.push(prop);
        objs.push(obj[prop]);
        depths.push(0);
    }
    while (objs.length > 0) {
        var prop = props.pop();
        var object = objs.pop();
        var depth = depths.pop();

        if (!result[depth]) {
            result[depth] = [];
        }
        if (lastDepth >= depth) {
            col++;
        }
        result[depth][col] = prop;
        lastDepth = depth;

        if (typeof object !== 'object') {
            continue;
        }
        for (var child in object) {
            tempStack.push(child);
        }
        while (tempStack.length > 0) {
            var child = tempStack.pop();
            props.push(child);
            objs.push(object[child]);
            depths.push(depth + 1);
        }
    }

    for (var i in result) {
        printLine(result[i]);
    }
}

@chenshuo
Copy link

from collections import OrderedDict

HEADERS = OrderedDict([
    ('Process', OrderedDict([
        ('CpuUsage', 0),
        ('VirtualMemory', 0),
        ('PrivateMemory', 0)
    ])),
    ('Subscriptions', OrderedDict([
        ('Order', OrderedDict([
            ('TotalCount', 0),
            ('LastMinute', 0),
            ('Failed', 0)
        ])),
        ('User', OrderedDict([
            ('TotalCount', 0),
            ('LastMinute', 0)
        ]))
    ]))]
)

# print headers
def printLine(array):
  for line in array:
    print ','.join(line).rstrip(',')


LOCATIONS = {}

def findWidth(obj, row, col):
  if type(obj) is OrderedDict:
    width = 0
    for name in obj:
      nw = findWidth(obj[name], row+1, col+width)
      LOCATIONS[(row, col+width)] = name
      width += nw
    return width
  else:
    return 1


def printHeader(obj):
  total_width = findWidth(obj, 0, 0)
  total_height = max(LOCATIONS)[0] + 1
  row = [''] * total_width
  array = [row[:] for x in range(total_height)]
  for item in LOCATIONS:
    array[item[0]][item[1]] = LOCATIONS[item]

  printLine(array)


printHeader(HEADERS)

@JuneAndGreen
Copy link

function printHeader(obj) {
    var output = [];

    var doFormat = function(input, deep, offset) {
        if(typeof input !== 'object') return 1;

        var arr = output[deep] || [];
        var prev = 0;
        var all = 0;
        if(!output[deep]) output.push(arr);
        Object.keys(input).forEach(function(key) {
            var item = input[key];
            arr[all + offset] = key;
            var length = doFormat(item, deep + 1, all + offset);
            all += length;
        });
        return all;
    };

    doFormat(obj, 0, 0);

    for(var i=0,len=output.length; i<len; i++) {
        output[i] = output[i].join(',');
    }

    return output.join('\n');
}

@bao-qian
Copy link

from collections import OrderedDict


def print_line(line: list):
    print(','.join(line))


def print_header(header_dict: dict):
    table = []
    width = 0

    def create_table(h=header_dict, height=0):
        nonlocal width

        for key in h:
            if len(table) <= height:
                table.append([])
            n = width - len(table[height])
            table[height] += [''] * n
            table[height].append(key)

            if type(h[key]) is OrderedDict:
                create_table(h[key], height + 1)

            width = max(len(table[height]), width)

    create_table()
    for line in table:
        print_line(line)


if __name__ == '__main__':
    HEADER_DICT = OrderedDict([
        ('Process', OrderedDict([
            ('CpuUsage', 0),
            ('VirtualMemory', 0),
            ('PrivateMemory', 0)
        ])),
        ('Subscriptions', OrderedDict([
            ('Order', OrderedDict([
                ('TotalCount', 0),
                ('LastMinute', 0),
                ('Failed', 0)
            ])),
            ('User', OrderedDict([
                ('TotalCount', 0),
                ('LastMinute', 0)
            ]))
        ]))]
    )
    print_header(HEADER_DICT)

python 版。对象的建立是扒的楼上 chenshuo 的。

@dangolol
Copy link

function printHeader(obj) {
  var header = [],
      level = 0;

  each(obj, header, level);

  for (var i = 0, len = header.length; i < len; i++) {
    printLine(header[i]);
  }
}

function each(obj, header, level, parent) {
  if (header[level] === undefined) {
    header[level] = [];
  }

  if (parent != null) {
    var index = header[level - 1].index(parent);

    if (index > 0 && header[level].length == 0) {
      header[level].length = index;
    }
  }

  for (var key in obj) {
    header[level].push(key);

    if (typeof obj[key] === "object") {
      each(obj[key], header, level + 1, key);
    }
  }

  if (level > 0 && header[level].length > header[level - 1].length) {
    header[level - 1].length = header[level].length;
  }
}

Array.prototype.index = function(obj) {
  var len = this.length - 1;

  while (this[len] !== obj && len >= 0) {
    len--;
  }

  return len;
};

@yszou
Copy link

yszou commented Mar 23, 2016

// | root     |
// | Process  |                               | Other    | Subscriptions
// | CpuUsage | VirtualMemory | PrivateMemory | CpuUsage | Order         |            |        | User
// |                                          |          | TotalCount    | LastMinute | Failed | TotalCount | What |    | LastMinute |
// |                                          |          |                                     |            | AA   | BB |

var data = {
    Process: {
        CpuUsage: 0,
        VirtualMemory: 0,
        PrivateMemory: 0
    },

    Other: {
        CpuUsage: 0,
    },

    Subscriptions: {
        Order: {
            TotalCount: 0,
            LastMinute: 0,
            Failed: 0
        },
        User: {
            TotalCount: 0,
            What: {
              AA: 0,
              BB: 0
            },
            LastMinute: 0
        }
    }
};

// 原始结构就是树
// 表格的高, depth, 深度
// 表格的宽, breadth, 广度 -> 不是树的广度, What 会被子节点撑开, 应该是延续之前一个节点的"列位"
// 遍历过程中可以得到 lv 和 column , 那就可以直接刚坐标了
// 列表尾部的 undefined 不会保留, 但是遍历过程中又是不知道最大广度的, 最后填坑吧

function findMax(data){
  var group = {}; // breadth, depth
  var result = [];

  function dig(obj, info, key, lv, column){

    //console.log(key, lv - 1 - 1, info['breadth'] + column - 1 - 1);
    if(!result[lv - 1 - 1]){result[lv - 1 - 1] = []};
    result[lv - 1 - 1][info['breadth'] + column - 1 - 1] = key;

    if (Object.prototype.toString.call(obj) !== '[object Object]'){ return }


    if( lv > info['depth']){ info['depth'] = lv }
    if( column > info['breadth']){ info['breadth'] = column }

    var count = -1;
    for(var k in obj){
      count += 1;
      dig(obj[k], info, k, lv+1, column + count);
    }
    info['breadth'] += count;
  }

  for(var k in data){
    group[k] = {breadth: 1, depth: 1};
    dig(data[k], group[k], k, 2, 1);
  }

  return [group, result];

}

function printHeader(obj, isPrint){
  var p = findMax({root: obj});
  var info = p[0]['root'];
  var data = p[1];
  var canvas = [];

  for(var i = 1, l = info['depth']; i < l; i++){
    canvas[i] = [];
    for(var j = 0, ll = info['breadth']; j < ll; j++){
      var cell = data[i][j] || '';
      if(cell.length < 15 && isPrint){ cell += (Array(15 - cell.length + 1).join(' '))}
      canvas[i][j] = cell;
    }
  }

  canvas.forEach(function(row){
    if(isPrint){
      console.log(row.join(' | '));
    } else {
      console.log(row.join(','));
    }
  });
}

printHeader(data, false);

@wdanxna
Copy link

wdanxna commented Mar 23, 2016

function printLine(array) {
    console.log(array.join(","));
}

function printHeader(obj) {
    function iter(obj, head, lines, line, padding) {
        if (head) {
            if (line > 0) {
                if (!lines[line - 1]) {
                    //previous line not exist.
                    var curline = [];
                    for (var i = 0; i < padding; i++) {
                        curline.push("");
                    }
                    curline.push(head);
                    lines.push(curline);
                } else {
                    // previous line exist.
                    var curline = lines[line - 1];
                    var residual = padding - lines[line - 1].length;
                    if (residual > 0) {
                        for (var i = 0; i < residual; i++) {
                            curline.push("");
                        }
                    }
                    curline.push(head);
                }
            }
        }
        if (typeof obj !== 'object') {
            return 1;
        }
        var curPadding = 0;
        for (var section in obj) {
            var p = iter(obj[section], section, lines, line + 1, padding + curPadding);
            curPadding += p;
        }
        return curPadding;
    }

    var lines = []
    iter(obj, "", lines, 0, 0);

    lines.forEach(printLine);
}

@LuoYY
Copy link

LuoYY commented Mar 23, 2016

function printLine(array){
    console.log(array.join(','));
}

function printHeader(obj) {
    var str = []; //string container
    objectPropTraversal(obj, str, 0, 0);
    for (var i in str){
        printLine(str[i]);
    }
}

function objectPropTraversal(obj, str, row, col) {
    if (typeof str[row] == 'undefined') {
        str[row] = [];
    }
    for(var value in obj){
        str[row][col] = value;
        if (typeof obj[value] == 'object') {
            col = objectPropTraversal(obj[value], str, row+1, col);
        }else{
            col++;
        }
    }
    return col;
}

@huaxinjiayou
Copy link

function printLine(array) {
    console.log(array.join(","));
}

function printHeader(obj) {
    // 根据 key 的深度展开对象,同时获取 deep 和 key 的哈希表
    // demo 结果为
    // ["0", "00", "01", "02", "1", "10", "100", "101", "102", "11", "110", "111"]
    // {0: "Process", 1: "Subscriptions", 10: "Order", 11: "User", 100: "TotalCount", 101: "LastMinute", 102: "Failed", 110: "TotalCount", 111: "LastMinute", 00: "CpuUsage", 01: "VirtualMemory", 02: "PrivateMemory"}
    var aOrder = [];
    var oOrder = {};
    fExpand(obj, '', aOrder, oOrder);

    // 最后一行输出的长度及具体信息
    // demo 结果
    // ["00", "01", "02", "100", "101", "102", "110", "111"]
    // 可以知道最长的一行长度 aRule.length
    var aRule = [];
    var sLast;
    aOrder.forEach(function (sItem, nIndex) {
        if (nIndex > 0 && sItem.indexOf(sLast) === 0) {
            aRule.pop();
        }
        aRule.push(sItem);
        sLast = sItem;
    });

    // 从 deep 为 1 开始遍历 aRule,然后逐行打印
    var nDeep = 1;
    while (true) {
        var aResult = [];
        var sLast = '';
        var nLastIndex = -1;
        aRule.forEach(function (sItem, nIndex) {
            var nLength = sItem.length;
            if (nLength < nDeep) {
                aResult.push('');
            } else if (nLength === nDeep) {
                aResult.push(oOrder[sItem]);
                nLastIndex = nIndex;
            } else {
                sItem = sItem.substr(0, nDeep);
                if (sItem !== sLast) {
                    sLast = sItem;
                    aResult.push(oOrder[sItem]);
                    nLastIndex = nIndex;
                } else {
                    aResult.push('');
                }
            }
        });

        // 去掉多余的空格
        aResult.length = nLastIndex + 1;
        if (aResult.length === 0) {
            break;
        }

        printLine(aResult);
        nDeep++;
    }
}


function fExpand(obj, sDeep, aOrder, oOrder) {
    var nIndex = 0;
    for (var sKey in obj) {
        var oVal = obj[sKey];
        var sOrder = sDeep + nIndex;
        aOrder.push(sOrder);
        oOrder[sOrder] = sKey;
        if (fIsObj(oVal)) {
            fExpand(oVal, sOrder, aOrder, oOrder);
        }
        nIndex++;
    }
}

function fIsObj(obj) {
    return Object.prototype.toString.call(obj) === '[object Object]';
}

@bao-qian
Copy link

js 版本,基本跟上面的 python 版本一样。

function printLine(array) {
    console.log(array.join(","));
}

function printHeader(obj) {
    var table = [];
    var width = 0;

    function create_table(header, height) {
        for (var key in header) {
            var line = table[height] || [];
            var n = width - line.length;
            line.push.apply(line, new Array(n).fill(''));
            line.push(key);

            if (typeof header[key] == "object") {
                create_table(header[key], height + 1)
            }
            width = Math.max(line.length, width);
            table[height] = line
        }
    }

    create_table(obj, 0);
    table.forEach(printLine);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment