🍄RawData
// 📅 revision history:
// 2022.07.28:
// - 將 mainColumns 放入 constructor() 的參數,存入 RawData
// - indexOfColumn 改存放於 RawData
// - _cell() 改為 cellValue()
// - 🐞 除蟲: Object.entries(this.data) 改為 this.data.entries() - (integer-base index)
// 🍄 RawData
// 負責分析原始資料
// 🔸 data: [[string]] // 原始資料(全部轉為字串)
// 🔸 mainColumns: [string] // 主要欄位(⭐ 由這些字串「開頭(startWith)」即可,不需一模一樣。)
// 🔸 indexOfColumn (dict) // 共有欄位索引
class RawData {
// custom class type name
get [Symbol.toStringTag]() { return 'RawData' }
// init
constructor(sheetName, mainColumns){ // data from data range values
// 🔸 .mainColumns
this.mainColumns = mainColumns;
let data = app.dataRangeValuesFromSheet(sheetName);
if(!data) throw new Error(`⛔ RawData: 找不到試算表「${sheetName}」的資料`);
// 🔸 .data
// normalize all cell values (remove all whitespaces)
this.data = data.map(row =>
row.map(value => (value + '').removeWhitespaces())
);
// 🔸 .indexOfColumn
this.parseColumnIndices(); // parse phase 1: 找共有欄位索引
// parse phase 2: 逐列抓出各班學生資料
// 🔸 app.students
this.parseAllRows();
}
// parse main column indices
parseColumnIndices(){
for(const [i, row] of this.data.entries()){
// if not header row, skip
if(!this.isHeaderRow(row)) continue;
// 🔸 .indexOfColumn
// now, it's a header row
this.indexOfColumn = this._indexDictForMainColumnsFromRow(row);
const n = Object.keys(this.indexOfColumn).length;
log(`ℹ️ 第 ${i + 1} 列:「${row}」`);
log(`ℹ️ 在第 ${i + 1} 列找到主要欄位「${this.mainColumns}」(共 ${n} 欄),索引值如下:`);
log(this.indexOfColumn);
// braak for-loop
break;
}
// if not found, throw error
if(!this.indexOfColumn) throw new Error(
`⛔ RawData._parseColumnIndices(): 在「原始報表」中找不到主要欄位「${this.mainColumns}」`
);
}
// find indices for main cols from row
_indexDictForMainColumnsFromRow(row){
let dict = {};
for(const colName of this.mainColumns){
const i = row.findIndex(x => x.startsWith(colName)); // ⭐️ 例如:原始「科目」名稱後面還有「編號」!
if(i < 0) throw new Error(`⛔ RawData._indexDictForMainColumnsFromRow:「${row}」沒有「${colName}」這個欄位名稱。`);
dict[colName] = i;
}
return dict;
}
// 🔸 儲存格資料:(for main columns)
cellValue(row, colName) {
return row[this.indexOfColumn[colName]];
}
// 🔸 parse all students data from rows
parseAllRows(){
for(const row of this.data){
if(this.isStudentRow(row)) Student.all.push(this._studentFromRow(row));
}
log(`ℹ️ 共找到 ${Student.all.length} 個學生資料。`);
log(`ℹ️ 第一個學生:${Student.all[0]}`);
}
// new student from row
_studentFromRow(row) {
return new Student(...this.mainColumns.map(col => this.cellValue(row, col)));
}
// 🔸 是否為表頭列(含有班級、座號、姓名等)
isHeaderRow(row){
// 對於每個主要欄位名稱(name),本列都有一個元素值(value)是以這個名稱開始的(startsWith)。
return this.mainColumns.every(name => row.some(value => value.startsWith(name)));
}
// 🔸 is student row
isStudentRow(row){
const classNo = +this.cellValue(row, '班級');
const seatNo = +this.cellValue(row, '座號');
if(!(100 < classNo && classNo < 320)) return false; // 不是班級
if(!(0 < seatNo && seatNo < 70)) return false; // 不是座號
if(!this.cellValue(row, '姓名')) return false; // 不是姓名
const rank = +this.cellValue(row, '名次');
if(!(0 < rank && rank < 4)) return false; // 不是前三名
return true;
}
}
Last updated