Skip to content

Instantly share code, notes, and snippets.

@JeffreyZhao
Last active July 17, 2019 10:47
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • 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

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

@Alaya-in-Matrix
Copy link

用Haskell试了下……省略了parse json的工作

{-# language DeriveFoldable, DeriveFunctor #-}
module Main where

data HeaderTree a = Leaf a 
                  | Node a [HeaderTree a]
                  deriving(Show, Foldable, Functor)

header = Node "Header" [process, sub]
    where process = Node "Process" [c, v, p]
          c       = Leaf "CpuUsage"
          v       = Leaf "VirtualMemory"
          p       = Leaf "PrivateMemory"
          sub     = Node "Subscription" [o, u]
          o       = Node "Order" [t, l, f]
          t       = Leaf "TotalCount"
          l       = Leaf "LastMinute"
          f       = Leaf "Failed"
          u       = Node "User" [ut, ul]
          ut      = Leaf "TotalCount"
          ul      = Leaf "LastMinute"

value :: HeaderTree a -> a
value (Leaf v)   = v
value (Node v _) = v

cvt :: (Show a) => HeaderTree a -> HeaderTree String
cvt = fmap cellPrint . cvt'
    where cellPrint (content, len) = show content ++ replicate len ','
          cvt' (Leaf x)    = Leaf (x, 1)
          cvt' (Node x ns) = Node (x, nc) ns'
              where ns' = map cvt' ns
                    nc  = sum $ map (snd . value) ns'

nextLevel :: HeaderTree String -> [HeaderTree String]
nextLevel (Leaf _)    = [Leaf ","]
nextLevel (Node _ ns) = ns

printHeader :: [HeaderTree String] -> [String]
printHeader level
    | line == replicate (length line) ',' = []
    | otherwise = line : printHeader (concatMap nextLevel level)
        where line = (reverse . (dropWhile (== ',')) . reverse) $ concatMap value level

main = mapM_ putStrLn $ tail $ printHeader [cvt header]

大概显示效果:

alaya@matrix[tmp] $ ghci test.hs  
GHCi, version 7.10.2: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling Main             ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> main 
"Process",,,"Subscription"
"CpuUsage","VirtualMemory","PrivateMemory","Order",,,"User"
,,,"TotalCount","LastMinute","Failed","TotalCount","LastMinute"
*Main>  

@ZTGeng
Copy link

ZTGeng commented Mar 22, 2016

function printHeader(obj) {
    var maxLen = 1;
    var objToHeader = function(obj, array, level) {
        if (!array[level]) {
            array[level] = [];
            if (level > 0) {
                for (var i = 0; i < array[level-1].length - 1; i++) {
                    array[level].push("");
                }
            }
        }
        var index = 0;
        for (var prop in obj) {
            if (index > 0) {
                while (array[level].length < maxLen) {
                    array[level].push("");
                }
                maxLen++;
            }
            array[level].push(prop);
            if (typeof obj[prop] === 'object') {
                array = objToHeader(obj[prop], array, level+1);
            }
            index++;
        }
        return array;
    }
    var header = objToHeader(obj, [], 0);
    for (var i in header) {
        printLine(header[i]);
    }
}

@fkysly
Copy link

fkysly commented Mar 22, 2016

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

function printHeader(obj) {
    var result = ''
    for (var key in obj) {
        var count = traversal(obj[key])
        result += key + new Array(count+1).join(',')
    }
    console.log(result)
}

printHeader(input)


function traversal(obj) {
    var count = 0
    for (var key in obj) {
        count += traversal(obj[key])
    }
    return max(count, 1)
}

function max(x, y) {
    return (x > y) ? x : y
}

ps:没有去末尾的逗号。

@jl-
Copy link

jl- commented Mar 22, 2016

function printLine(array) {
  console.log(array.join(','))
}
function printHeader(obj) {
    function getPrevLength(obj) {
        if (!obj || typeof obj !== 'object') return 1;
        return Object.keys(obj).reduce((length, key) => length + getPrevLength(obj[key]), 0);
    }
    function addHeaderTerm(lines, obj, m, n) {
        const row = lines[m] || (lines[m] = []);
        const keys = Object.keys(obj);
        keys.reduce((prevLengthGap, key, index) => {
            const term = obj[key];
            const prevKey = keys[index - 1];
            if (prevKey) {
                prevLengthGap = prevLengthGap + getPrevLength(obj[prevKey]) - 1;
            }
            const nn = n + index + prevLengthGap;
            row[nn] = key;
            if (typeof term === 'object') {
                addHeaderTerm(lines, term, m + 1, nn);
            }
            return prevLengthGap;
        }, 0);
    }
    const lines = [];
    addHeaderTerm(lines, obj, 0, 0);
    lines.forEach(printLine);
}

@fanthos
Copy link

fanthos commented Mar 22, 2016

var toCsv = function(o) {
    let loc = 0;
    let ret = [];
    var getChild = function(c, deep) {
        Object.keys(c).forEach(function(c1){
            let c2 = c[c1];
            if(!ret[deep])ret[deep]=[];
            if(typeof(c2) == 'object') {
                ret[deep][loc] = c1;
                getChild(c2, deep+1);
            } else {
                ret[deep][loc] = c1;
                loc++;
            }
        })
    }
    getChild(o, 0);
    return ret;
};

@f48vj
Copy link

f48vj commented Mar 22, 2016

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

    function cal(object, depth) {
      if(typeof object !== 'object') {
        count += 1;
        return;
      }

      if(header[depth] === undefined) {
        header[depth] = [];
      }
      for(var key in object) {
        header[depth][count] = key;
        cal(object[key], depth + 1);
      }
    }

    cal(obj, 0);
    for(var index in header) {
      printLine(header[index]);
    }
}

@lust4life
Copy link

ugly recursion version

function printHeader(obj){
    var arr = [];
    fk(null,obj,-1,0,arr);
    arr.forEach(pl);
};


function fk(oKey,oValue,depth,nth,arr){
    if(!arr[depth]){
        arr[depth] = [];
    }

    if(!arr[depth+1]){
        arr[depth+1] = [];
    }

    if(oKey){
        var nextDepthLength = arr[depth+1].length;
        var length = nth > nextDepthLength ? nth : nextDepthLength ;
        arr[depth][length] = oKey;
    }

    if(typeof oValue === 'object'){
        var currentDepthLength = arr[depth].length-1;
        Object.keys(oValue).forEach((key,num) => {
            fk(key,oValue[key],depth+1,currentDepthLength + num,arr);
        })
    }
}

@aukw
Copy link

aukw commented Mar 22, 2016

我算法基础不行,代码看起来不够XX

function printHeader(json){
    var header = getHeaderFromJson(json);
    var i = 0, j = 0, len = header.data.length, arr = [], col=[], lastColName = "";
    for(i = 0; i < header.rows; i++){
        col = [], lastColName="";
        for(j = 0; j < len; j++){
            tarr = header.data[j].split(".");
            col[j] =  tarr[i] != lastColName ? tarr[i] : "" ;
            lastColName = tarr[i];
        }
        console.log(col.join(","));
    }
}

function getHeaderFromJson(json){
    var arr = [];
    hash2flat("", json, arr);
    var i,len, header = { rows: 0, cols: arr.length, data: arr};
    for(i=0, len=arr.length; i<len; i++){
        header.rows = Math.max(header.rows, arr[i].split(".").length);
    }
    return header;
}

function hash2flat(key, hash, arr){
    var newKey="";
    for( i in hash){
        newKey = key == "" ? i : key+"."+i;
        if(hash[i].constructor == Object ){
            hash2flat(newKey, hash[i], arr);
        } else {
            arr[arr.length] = newKey;
        }
    }
    return arr;
}

@shiye515
Copy link

function printHeader(obj) {
    var table = [];
    var index = 0;
    (function buildTable(obj, level) {
        table[level] = table[level] || [];
        Object.keys(obj).forEach((key) => {
            table[level][index] = key;
            if ('[object Object]' === Object.prototype.toString.call(obj[key])) {
                let newLevel = level + 1;
                buildTable(obj[key], newLevel)
            } else {
                index += 1;
            }
        })
    })(obj, 0)

    table.forEach(printLine)
}

@myst729
Copy link

myst729 commented Mar 22, 2016

function printHeader(obj) {
  var result = []
  var offset = 0

  void function walk(o, level) {
    if (result[level] === undefined) {
      result[level] = []
    }

    Object.keys(o).forEach(function(key) {
      result[level][offset] = key

      if (typeof o[key] === 'object') {
        walk(o[key], level + 1)
      } else {
        offset++
      }
    })
  }(obj, 0)

  result.forEach(printLine)
}

我擦……我跟楼上答案怎么这么像……

@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