使用字符串(不是HTML)以表格格式显示对象数组。

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用字符串(不是HTML)以表格格式显示对象数组。相关的知识,希望对你有一定的参考价值。

我不想使用html平板标签,因为输出将显示在一个字符串字段中。请看下面的代码片段。预期的输出应该显示为类似于一个带键的表,作为头和对象的值分别复制到头(键)。请看下图的预期输出。对我来说,这里棘手的部分是当我把长字符串作为值时,对齐方式会受到干扰。

注意:我的最终目标是节省一些字符以避免字符串字段的溢出,所以我想到了下面的表格方法。我的字符串字段只有4000个字符的长度。如果有更好的方法以字符串格式显示对象,并且对最终用户来说是可读的,那么我不介意改变格式,只要我们不浪费任何字符来使它漂亮。

enter image description here

var data = 
    "table-info":[
        
            "Hostname":"server 756",
            "Slot":"NC1",
            "VLAN":"test 12",
            "Port Type":"Access",
            "Port":"port 12",
            "Switch":"switch12"
        ,
        
            "Hostname":"",
            "Slot":"NI4",
            "VLAN":"test 13",
            "Port Type":"Tag",
            "Port":"port 13",
            "Switch":"switch13"
        ,
        
            "Hostname":"",
            "Slot":"PC2: 2",
            "VLAN":"test 14",
            "Port Type":"Access",
            "Port":"port 14",
            "Switch":"switch14"
        ,
        
            "Hostname":"",
            "Slot":"NI4",
            "VLAN":"test 15",
            "Port Type":"Tag",
            "Port":"port 15",
            "Switch":"switch15"
        ,
        
            "Hostname":"",
            "Slot":"PCI: B",
            "VLAN":"test 16",
            "Port Type":"Tag",
            "Port":"port 16",
            "Switch":"switch16"
        ,
        
            "Hostname":"",
            "Slot":"PI3: A",
            "VLAN":"test 17",
            "Port Type":"Tag",
            "Port":"port 17",
            "Switch":"switch17"
        ,
        
            "Hostname":"server 757",
            "Slot":"NC1",
            "VLAN":"test 12",
            "Port Type":"Access",
            "Port":"port 18",
            "Switch":"switch18"
        ,
        
            "Hostname":"",
            "Slot":"NC4",
            "VLAN":"test 13",
            "Port Type":"Tag",
            "Port":"port 19",
            "Switch":"switch19"
        ,
        
            "Hostname":"",
            "Slot":"PCI2: 2",
            "VLAN":"test 14",
            "Port Type":"Access",
            "Port":"port 20",
            "Switch":"switch20"
        ,
        
            "Hostname":"",
            "Slot":"NI4",
            "VLAN":"test 15",
            "Port Type":"Tag",
            "Port":"port 21",
            "Switch":"switch21"
        
    ]
;

var tableInfo = data['table-info'],
headers = Object.keys(tableInfo[0]),
tableInfoLen = tableInfo.length,
headersLen = headers.length;
var result = headers.join('\t');

for (var i = 0; i < tableInfoLen; i++) 
    result += '\n';
    for (var j = 0; j < headersLen; j++ ) 
        if (tableInfo[i][headers[j]] == '') 
            result += tableInfo[i][headers[j]] + '\t'+'\t';
        
        else 
             result += tableInfo[i][headers[j]] + '\t';
        
    


console.log(result);

通过使用下面的代码段,我实现了图中所示的预期输出。有一个额外的函数(padding),它可以根据给定字符串的长度添加空格,但是字符又被吃掉了,这不是我想要的。

var data = 
    "table-info":[
        
            "Hostname":"server 756",
            "Slot":"NC1",
            "VLAN":"test 12",
            "Port Type":"Access",
            "Port":"port 12",
            "Switch":"switch12"
        ,
        
            "Hostname":"",
            "Slot":"NI4",
            "VLAN":"test 13",
            "Port Type":"Tag",
            "Port":"port 13",
            "Switch":"switch13"
        ,
        
            "Hostname":"",
            "Slot":"PC2: 2",
            "VLAN":"test 14",
            "Port Type":"Access",
            "Port":"port 14",
            "Switch":"switch14"
        ,
        
            "Hostname":"",
            "Slot":"NI4",
            "VLAN":"test 15",
            "Port Type":"Tag",
            "Port":"port 15",
            "Switch":"switch15"
        ,
        
            "Hostname":"",
            "Slot":"PCI: B",
            "VLAN":"test 16",
            "Port Type":"Tag",
            "Port":"port 16",
            "Switch":"switch16"
        ,
        
            "Hostname":"",
            "Slot":"PI3: A",
            "VLAN":"test 17",
            "Port Type":"Tag",
            "Port":"port 17",
            "Switch":"switch17"
        ,
        
            "Hostname":"server 757",
            "Slot":"NC1",
            "VLAN":"test 12",
            "Port Type":"Access",
            "Port":"port 18",
            "Switch":"switch18"
        ,
        
            "Hostname":"",
            "Slot":"NC4",
            "VLAN":"test 13",
            "Port Type":"Tag",
            "Port":"port 19",
            "Switch":"switch19"
        ,
        
            "Hostname":"",
            "Slot":"PCI2: 2",
            "VLAN":"test 14",
            "Port Type":"Access",
            "Port":"port 20",
            "Switch":"switch20"
        ,
        
            "Hostname":"",
            "Slot":"NI4",
            "VLAN":"test 15",
            "Port Type":"Tag",
            "Port":"port 21",
            "Switch":"switch21"
        
    ]
;

 String.prototype.padding = function(n, c)
 
         var val = this.valueOf();
         if ( Math.abs(n) <= val.length ) 
                 return val;
         
         var m = Math.max((Math.abs(n) - this.length) || 0, 0);
         var pad = Array(m + 1).join(String(c || ' ').charAt(0));
         //console.log(pad);

         return (n < 0) ? pad + val : val + pad;
 ;

var tableInfo = data['table-info'],
headers = Object.keys(tableInfo[0]),
tableInfoLen = tableInfo.length
headersLen = headers.length;

var result = '';
for (var k = 0; k < headers.length; k++ ) 
  result += headers[k].padding(15)+'\t';

for (var i = 0; i < tableInfoLen; i++) 
    result += '\n';
    for (var j = 0; j < headersLen; j++ ) 
        if (tableInfo[i][headers[j]] == '') 
            result += tableInfo[i][headers[j]].padding(15)+ '\t';
        
        else 
            result += tableInfo[i][headers[j]].padding(15) + '\t';
        
    


console.log(result);
答案

在建立表格之前,你需要测量所有将要显示的文本。

我还添加了一个文本调整枚举,这样你就可以对标题栏进行居中调整。


更新

  1. 动态地给每一行添加了一个索引字段,以显示数字的自动调整。
  2. 将众多功能转换为结构良好的ES6类。
  3. 创建了一个单独的测量列宽的方法,并保护了对未定义值的格式化。
  4. 增加了截断支持

好了,我就不说了,但你可以看到,它的可扩展性很强。

const main = () => 
  let jsonData = getJson()['table-info'].map((item, index) => 
    return  Index : (index + 1), ...item ; // Add index to front 
  );

  let table = new TableFormater(
    scanAll : true, // We would miss some fields without it
    spacer: ' | ',  // Divide the columns with a pipe
    columns: 
      'Port Type' : 
        align: TextAlignment.CENTER
      ,
      'ExtraLongKeyAlsoTruncated' : 
        limit: 6 // Limit the number of characters
      
    
  ).fromJson(jsonData);
  
  console.log(table);
;

/* 
 * @description An enum representing text alignment
 */
const TextAlignment = Object.freeze(
  LEFT   : Symbol("LEFT"),
  RIGHT  : Symbol("RIGHT"),
  CENTER : Symbol("CENTER")
);

/* 
 * @description A class used for converting data into text tables
 */
class TableFormater 
  constructor(options) 
    this.opts = Object.assign(, TableFormater.DEFAULT_OPTIONS, options);
  

  /* @public */
  fromJson(json) 
    const fields    = this.__gatherFields(json, this.opts.scanAll),
          colWidths = this.__measureColumns(json, fields);
    return [
      fields.map((field, index) => 
        return this.__format(field, field, index, colWidths, 
          align: TextAlignment.CENTER // Force headers to be centered
        );
      ).join(this.opts.spacer),
      json.map(record => 
        return fields.map((field, index) => 
          return this.__format(record[field], field, index, colWidths);
        ).join(this.opts.spacer);
      ).join('\n')
    ].join('\n');
  

  /* @private */
  __format(value, field, index, colWidths, overrides) 
    return this.__alignContent(value,
      Object.assign(this.__generateMetaData(field, index, colWidths),
        overrides || ));
  

  /* @private */
  __alignContent(content, metadata) 
    let isNumeric = !isNaN(content);
    content = ('' + (content || '')).trim();
    if (content.length === 0) 
      return content.padEnd(metadata.maxWidth); // If empty, pad away!
    
    if (metadata.limit !== -1 && content.length > metadata.limit) 
      content = content.substring(0, metadata.limit - 1) + '…';
    
    if (content.length === metadata.maxWidth) 
       return content; // Optimization
    
    let alignment = metadata.align || TextAlignment.LEFT;
    if (isNumeric && metadata.align == null) 
      alignment = TextAlignment.RIGHT;
    
    switch (alignment) 
      case TextAlignment.RIGHT:
        return content.padStart(metadata.maxWidth);
      case TextAlignment.CENTER:
        let diff = metadata.maxWidth - content.length;
        let offset = Math.floor(metadata.maxWidth - (diff / 2));
        return content.padStart(offset).padEnd(metadata.maxWidth);
      case TextAlignment.LEFT:
      default:
        return content.padEnd(metadata.maxWidth);
    
  

  /* @private */
  __generateMetaData(field, index, colWidths) 
    return Object.assign(
      maxWidth: colWidths[index],
      limit: -1,
      field: field
    , this.opts.columns[field]);
  

  /* @private */
  __gatherFields(data, scanAllRecords) 
    return scanAllRecords
      ? [...new Set(data.flatMap(record => Object.keys(record)))]
      :  Object.keys(data[0]);
  

  /* @private */
  __measureColumns(data, fields) 
    let colWidths = fields.map(field => this.__measureValue(field, field));
    data.forEach(item => 
      fields.forEach((field, index) => 
        colWidths[index] = Math.max(colWidths[index],
          this.__measureValue(item[field], field));
      );
    );
    return colWidths;
  
  
  /* @private */
  __measureValue(value, field) 
    if (value == null) return 0;
    value = ('' + value).trim();
    let column = this.opts.columns[field] || ;
    return column.limit !== -1 && value.length > column.limit
      ? column.limit : value.length;
  


/* @static */
TableFormater.DEFAULT_OPTIONS = 
  columnSpacer : ' ',
  columns      : ,
  scanAll      : false
;

const getJson = () => 
  return 
    "table-info": [
      "Hostname": "server 756",
      "Slot": "NC1",
      "VLAN": "test 12",
      "Port Type": "Access",
      "Port": "port 12",
      "Switch": "switch12"
    , 
      "Slot": "NI4",
      "VLAN": "test 13",
      "Port Type": "Tag",
      "Port": "port 13",
      "Switch": "switch13"
    , 
      "Slot": "PC2: 2",
      "VLAN": "test 14",
      "Port Type": "Access",
      "Port": "port 14",
      "Switch": "switch14",
      // This is to demonstrate 'scanAll' and 'limit'
      "ExtraLongKeyAlsoTruncated": "I will be truncated!"
    , 
      "Slot": "NI4",
      "VLAN": "test 15",
      "Port Type": "Tag",
      "Port": "port 15",
      "Switch": "switch15"
    , 
      "Slot": "PCI: B",
      "VLAN": "test 16",
      "Port Type": "Tag",
      "Port": "port 16",
      "Switch": "switch16"
    , 
      "Slot": "PI3: A",
      "VLAN": "test 17",
      "Port Type": "Tag",
      "Port": "port 17",
      "Switch": "switch17"
    , 
      "Hostname": "server 757",
      "Slot": "NC1",
      "VLAN": "test 12",
      "Port Type": "Access",
      "Port": "port 18",
      "Switch": "switch18"
    , 
      "Slot": "NC4",
      "VLAN": "test 13",
      "Port Type": "Tag",
      "Port": "port 19",
      "Switch": "switch19"
    , 
      "Slot": "PCI2: 2",
      "VLAN": "test 14",
      "Port Type": "Access",
      "Port": "port 20",
      "Switch": "switch20"
    , 
      "Slot": "NI4",
      "VLAN": "test 15",
      "Port Type": "Tag",
      "Port": "port 21",
      "Switch": "switch21"
    ]
  
;

main();
.as-console-wrapper 
  top: 0;
  max-height: 100% !important;
  


.as-console-wrapper .as-console-row-code,
.as-console-wrapper .as-console-row:after 
  font-size: 0.8em;

以上是关于使用字符串(不是HTML)以表格格式显示对象数组。的主要内容,如果未能解决你的问题,请参考以下文章

jQuery Ajax 调用返回 JSON 字符串而不是对象数组

ng-repeat不以表格格式显示数据

如何以表格格式显示json数组? [复制]

一个表格中选定的tr,显示在另一个表格中

Pyspark:以表格格式显示火花数据框

将结构数组转换为字符串数组以显示为表格