纯JS实现排列整齐的数据表格

本文是在阅读《Eloquent JavaScript》第二版第六章时看到的示例,感觉十分有趣且有代表性,故在此留下笔记。


//1、传入多行,

//2、map遍历每行,返回一个新数组(里面的数组元素个数与行数对应)
//3、reduce计算每行中各个Cell的最大高度,返回一个值(该行的最大高度)
//-->结果得到一个含有每行最大高度的新数组
function rowHeights(rows) {
  return rows.map(function(row) {
    return row.reduce(function(max, cell) {
      return Math.max(max, cell.minHeight());
    }, 0);
  });
}
//1、传入多行,
//2、根据第一行(row[0])知道当前表格的总列数,并依次作为遍历次数的依据(若rows[0]的列数为3,则遍历3次)
//3、以第2步获得当前列索引(i)为标准,对每行的该列进行比较,比较得出该列的最大宽度(如当前i的值为0,则表示遍历表格各行的第一列)
function colWidths(rows) {
  return rows[0].map(function(_, i) {
    return rows.reduce(function(max, row) {
      return Math.max(max, row[i].minWidth());
    }, 0);
  });
}
function drawTable(rows) {
  
  var heights = rowHeights(rows);//获取存储某一Cell高和宽的数组
  var widths = colWidths(rows);

  function drawLine(blocks, lineNo) {
  //根据传入的blocks的长度进行遍历,本例中blocks有三列(name、height、country),所以遍历三次
  //返回传入的lineNo确定要进行处理的是该Cell的第几行,如该行单元格行数为2,如 name  height country,则lineNo为1时,就是对
  //  ----- ------ -------
  //name、height、country三个字符串进行处理,最终得到一个新数组["name        ","height","country      "],利用join方法合成为一个字符串,中间加入一个空格" "进行分隔
  //即第一行Cell的第一行内容最终输出结果为"name         height country      ",其中name和height之间有13个空格(加上间隔),height和country之间仅一个(country后有六个空格)  
    return blocks.map(function(block) {
      return block[lineNo];
    }).join(" ");
  }


  function drawRow(row, rowNum) {
  //对行内Cell进行遍历,得到同行Cell(加入padding后)共同组成的blocks,
  //blocks是个二维数组,若该行中的Cell最多有两行,则其表现为[Array[2], Array[2], Array[2]]
    var blocks = row.map(function(cell, colNum) {
      return cell.draw(widths[colNum], heights[rowNum]);
    });
    //当前单一Cell占几行,就遍历几次,若该Cell占两行,则lineNo为2,需要调用drawLine两次,
    //最终获得一个数组,包含Cell中两行对应的的字符串,用join方法合并为一个字符串,用\n分割换行
    return blocks[0].map(function(_, lineNo) {
      return drawLine(blocks, lineNo);
    }).join("\n");
  }
  //对所有表格的行进行遍历,得出每行对应的字符串组成的数组,同样用join方法合并为一个字符串
  return rows.map(drawRow).join("\n");
}
//用于对不满足所在列最小宽度要求的单元格,加上padding,padding为" "
function repeat(string, times) {
  var result = "";
  for (var i = 0; i < times; i++)
    result += string;
  return result;
}
//Cell的构造函数
function TextCell(text) {
  this.text = text.split("\n");
}
//返回该Cell里各行中的最大宽度作为该cell的最小行宽
TextCell.prototype.minWidth = function() {
  return this.text.reduce(function(width, line) {
    return Math.max(width, line.length);
  }, 0);
};
//返回该Cell对应的行数(实际上是数组的长度,因为在构造函数中已经用split将字符串变成数组)
TextCell.prototype.minHeight = function() {
  return this.text.length;
};
//返回Cell中各行文本在加入padding后组成的数组
TextCell.prototype.draw = function(width, height) {
  var result = [];
  for (var i = 0; i < height; i++) {
    var line = this.text[i] || "";
    result.push(line + repeat(" ", width - line.length));
  }
  return result;
};
//扩展Cell(表头)的构造函数,加入下划线
function UnderlinedCell(inner) {
  this.inner = inner;
}
UnderlinedCell.prototype.minWidth = function() {
  return this.inner.minWidth();
};
//因为加上下划线有两行,所以高度+1
UnderlinedCell.prototype.minHeight = function() {
  return this.inner.minHeight() + 1;
};
UnderlinedCell.prototype.draw = function(width, height) {
  return this.inner.draw(width, height - 1)
    .concat([repeat("-", width)]);
};


//RTextCell集继承自TextCall,构造函数能让RTextCell继承TextCell的私有属性
function RTextCell(text) {
  TextCell.call(this, text);
}
//使用原型链继承,使RTextCell继承TextCell的原型属性
//Object.create能够创建特定原型的实例
RTextCell.prototype = Object.create(TextCell.prototype);
//重写TextCell的draw方法
RTextCell.prototype.draw = function(width, height) {
  var result = [];
  for (var i = 0; i < height; i++) {
    var line = this.text[i] || "";
    result.push(repeat(" ", width - line.length) + line);
  }
  return result;
};
//将对象数组转化为排列整齐的数据表
function dataTable(data) {
  //Object.keys方法返回对象包含的(可枚举的)属性数组,因为对象的属性都是相同的,所以只需要数组中的第一个对象即可
  var keys = Object.keys(data[0]);
  //根据属性数组生成表头
  var headers = keys.map(function(name) {
    return new UnderlinedCell(new TextCell(name));
  });
  //遍历各对象
  var body = data.map(function(row) {
  //遍历对象中的属性数组,根据当前属性值的类型,实例化不同的表格元素(这里的不同指左右对齐)
    return keys.map(function(name) {
      var value = row[name];
      // This was changed:
      if (typeof value == "number")
        return new RTextCell(String(value));
      else
        return new TextCell(String(value));
    });
  });
  //最终
  return [headers].concat(body);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值