Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@twlca
Last active July 4, 2017 07:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save twlca/80bfa963c016689888d43626b907faa3 to your computer and use it in GitHub Desktop.
Save twlca/80bfa963c016689888d43626b907faa3 to your computer and use it in GitHub Desktop.
// 將資料依指定的 property 組成群組
// 要求:
// 1. 列印收據(點陣式)
// 2. 報表依捐款人、捐款金額、手續費、實收金額列印
// 3. 可供期間查詢:包含螢幕查詢及報表
// 4. 以捐款人、捐款日期、捐款種類、捐款管道分類,日期則以倒序法排列(最近的日期在最上方)
// 5. 捐款管道、手續費、捐款種類可以由使用者設定
// 6. 捐款人快速查詢:姓名排列、連絡地址排列(分類)
// 7. 捐款人組織或個人分類
var donators = [
{"id":"001",
"name": "Thomas Jao",
"address": "Taipei",
"sex": "male"},
{"id": "002",
"name": "Sally Yang",
"address": "Taipei",
"sex": "female"},
{"id": "003",
"name": "Teresa Zho",
"address": "Taitung",
"sex": "female"},
{"id": "004",
"name": "James Zheng",
"address": "Taoyuang",
"sex": "male"},
{"id": "005",
"name": "Marry Chen",
"address": "Kaoxiong",
"sex": "female"},
{"id": "006",
"name": "James Fu",
"address": "Hualiang",
"sex": "male"},
{"id": "007",
"name": "Jerry Jiang",
"address": "Hualiang",
"sex": "male"},
{"id": "008",
"name": "Jane Chen",
"address": "Taitung",
"sex": "female"}
];
var events = [
{"event_id": "001",
"date": "2014-6-30",
"amount": 20000,
"donator_id": "004",
"via": "V002",
"usage": "general"},
{"event_id": "002",
"date": "2014-6-24",
"amount": 3000,
"donator_id": "002",
"via": "V001",
"usage": "general"},
{"event_id": "003",
"date": "2015-4-30",
"donator_id": "007",
"amount": 1500,
"via": "V001",
"usage": "type 2"},
{"event_id": "004",
"date": "2015-10-22",
"amount": 2000,
"donator_id": "005",
"via": "V001",
"usage": "type 3"},
{"event_id": "005",
"date": "2015-4-30",
"amount": 10000,
"donator_id": "004",
"via": "V002",
"usage": "type 3"}
];
var usage = {"U001": "老人照顧", "U002": "兒童就學", "U003": "偏鄉醫療", "U004": "緊急救難"};
var via = {"V001": "郵局劃撥", "V002": "銀行匯款"};
// Function to group data by specific property key 資料群組,將同類資料集合成同一陣列
// @param: obj 要群組的物件
// @param: prop 依何種條件群組
groupBy = function( obj, prop ) {
var i = 0, val, index, values = [], result = [];
for (; i < obj.length; i++ ) {
val = obj[i][prop];
index = values.indexOf( val );
if ( index > -1 ) {
result[index].push( obj[i] );
} else {
values.push( val );
result.push( [obj[i]] );
}
}
return result;
}
// 排序日期:最近的日期在最前面
// 在 https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/sort 排列 maps
// map.sort(function(a, b) {
// return +(a.value > b.value) || +(a.value === b.value) - 1;
// });
// 似乎可以直接取代列函數,是極聰明的寫法。
datesObj.sort( function(a,b) {
if ( a.year > b.year ) {
return -1;
}
if ( a.year < b.year ) {
return 1;
}
// compar month if same year
if ( a.month > b.month ) {
return -1;
}
if ( a.month < b.month ) {
return 1;
}
// compare dates if both year and month the same
if ( a.day > b.day ) {
return -1;
}
if ( a.day < b.day ) {
return 1;
}
return 0;
});
// 顯示過去 n 天日期(含星期),並根據顯示模式,顯示西洋曆或民國香
var s = new Date().getTime();
function lastFiveDays(n, mode) {
var weekdays = ['日', '一', '二', '三', '四', '五', '六'];
var days = [];
for (var i = (n - 1); i > -1; i-- ) {
var d = new Date( s - i * 3600 * 24 * 1000);
days.push( d );
}
return days.map( function(item) {
return (item.getFullYear() - (mode == 'western' ? 0 : 1911)) + '-' + (item.getMonth() + 1) + '-' + item.getDate() + ' (' + weekdays[item.getDay()] + ')';
});
}
lastFiveDays(12, 'minguo');
// 對齊金額
// 顯示金額時,讓個位數對齊
<div class="lbl">
金額大小 <span class="amount">1,030,000</span>
</div>
<div class="lbl">
金額大小 <span class="amount">1,200</span>
</div>
<style>
.lbl { font-size: 28px; font-family: STKaiti; display: table-row; }
.amount { font-family: helvetica; font-size: 24px; display: table-cell; text-align: right; width: 5em; }
// 顯示金額時,以三位一撇節顯示
var n = 2542000;
var value = n.toLocaleString( 'zh-TW', {minimumFractionDigits: 0} );
p.textContent = value;
// or
function formatNumber( num, digits ) {
return num.toLocaleString( 'zh-TW', {minimumFractionDigits: digits});
};
var numbers = [ 25000000, 500000, 83002, 4738, 200, 60, 7];
numbers.map( function(item) {
return formatNumber(item, 0);
});
// ["25,000,000", "500,000", "83,002", "4,738", "200", "60", "7"]
// 例: 2542000 顯示成 2,542,000
// padding number
// 為了要將數字轉换成國字大寫金額,必需先將數字填充至八位數
// 需判別是否為文字串、正數且為整數
function padding( num ) {
if ( typeof num === 'number' && num >= 0 && Number.isInteger( num ))
return '00000000'.substr( 0, ( 8 - num.toString().length )) + num.toString();
// or return '00000000'.slice( 0, -("" + num).length) + ("" + num);
else
console.log( '函式引數必需為正整數。您輸入的引數可能是文字串、浮點數或者是負整數');
}
// split 8-digit number string
// 國字大寫金額是以四位數為一組,區分億、wang 及個位數
// 因此區分八位數字字串成上四位數字組及下四位數字組,再決定加上的單位值
function segmentNum( numStr ) {
return {
'upperDigits': numStr.substr(0, 4),
'lowerDigits': numStr.substr(4, 8)
}
}
// 以下的方法是否會更好?
var numbers = ['00002345', '10023456', '00123456', '09800000'].map( function(item) {
var answer = item.match(/.{1,4}/g);
return {
upper: answer[0],
lower: answer[1]
};
});
// 去除額外的‘零’以符合國字大寫慣例
var upperDigitsGlyph = ['零', '壹', '貳', '參', '肆', '伍', '陸', '㭍', '捌', '玖'];
var units = ['仟', '佰', '拾', ''];
partialDigits.split('').map( function(item, index) {
return upperDigitsGlyph[item] + (item == '0' ? '' : units[index]);
}).join('').replace(/零{2,7}/,'零','g').replace(/零$/,'').replace(/^零/,'');
// 産生輸出表格
function genTable( data ){
var tbl = document.createElement('table');
var titles = '<tr>' + keys(data[0]).map( function(item){
      return item =='經由' ? '' : '<td>' + item + '</td>';
}).join('') + '</tr>';
var result = data.map( function(item) {
return '<tr><td>' + item.捐款日期 + '</td><td>' + item.捐款人 + '</td><td>' + item.捐款金額 + '(' + item.經由 + ')</td><td>' + item.用途 + '</td></tr>';
}).join('');
tbl.innerHTML = titles + result;
body.appendChild(tbl);
}
// 產生輸出表格所需的資料,其中 "捐款人", "用途" 及 "經由" 是以代碼分別儲放於 donators, usage, via 中,所以以特殊方式找出資料
events.map( function(item) {
return {
捐款日期: item.date,
捐款人: donators.find( function(item1) { return item1.id == item.donator_id; } ),
捐款金額: item.amount,
經由: via[item.via],
用途: usage[item.usage]
};
});
// grouping elements in array by multiple properties
var list = [
{name: "1", lastname: "foo1", age: "16"},
{name: "2", lastname: "foo", age: "13"},
{name: "3", lastname: "foo1", age: "11"},
{name: "4", lastname: "foo", age: "11"},
{name: "5", lastname: "foo1", age: "16"},
{name: "6", lastname: "foo", age: "16"},
{name: "7", lastname: "foo1", age: "13"},
{name: "8", lastname: "foo1", age: "16"},
{name: "9", lastname: "foo", age: "13"},
{name: "0", lastname: "foo", age: "16"}
];
// expect to get result as
var result = [
[
{name: "1", lastname: "foo1", age: "16"},
{name: "5", lastname: "foo1", age: "16"},
{name: "8", lastname: "foo1", age: "16"}
],
[
{name: "2", lastname: "foo", age: "13"},
{name: "9", lastname: "foo", age: "13"}
],
[
{name: "3", lastname: "foo1", age: "11"}
],
[
{name: "4", lastname: "foo", age: "11"}
],
[
{name: "6", lastname: "foo", age: "16"},
{name: "0", lastname: "foo", age: "16"}
],
[
{name: "7", lastname: "foo1", age: "13"}
]
];
// 以下是個極為聰明的方法,利用 callback 作為引數輸數,可以做到最有彈性
function groupBy( array , f )
{
var groups = {};
array.forEach( function( o )
{
var group = JSON.stringify( f(o) );
groups[group] = groups[group] || [];
groups[group].push( o );
});
return Object.keys(groups).map( function( group )
{
return groups[group];
})
}
var result = groupBy(list, function(item)
{
return [item.lastname, item.age];
});
// Debounce
// 若選擇 radio/checkbox 並作為輸入選項,必需使用 debounce 以避免重複輸入
var showSelection = function() {
var p = document.getElementById('content');
if ( this.checked ) {
arr.push( this.value );
p.textContent = arr.join(', ');
return false;
} else {
arr.splice( arr.indexOf( this.value ), 1);
p.textContent = arr.join(', ');
return false;
}
}
var debounce = function(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if ( !immediate ) func.apply( context, args );
};
var callNow = immediate && !timeout;
clearTimeout( timeout );
timeout = setTimeout( later, wait );
if ( callNow ) func.apply( context, args );
};
}
// use debounce on eventListener
var checkChange = debounce( showSelection, 250 );
checks.forEach( function(item) {
item.addEventListener( 'change', checkChange );
});
// 經由 debounce 程序才可解決重複輸入的問題
}
// 產生映射出的資料
var data = events.map( function(item) {
return {
捐款日期: item.date,
捐款人: donators.find( function(entry) { return entry.id == item.donator_id; } ).name,
捐款金額: item.amount,
經由: via[item.via],
用途: usage[item.usage]
};
});
// 產生 Block 輸出
data.map( function(item, index) {
var div = document.createElement('div');
div.setAttribute('style', 'border: 1px solid #999; padding: 8px 12px; border-radius: 6px; font-size: 28px; font-family: STKaiti;');
var result = keys(item).map( function( props ) {
return '<p>' + props + ': ' + item[props] + '</p>';
}).join('');
div.innerHTML = result;
body.appendChild(div);
});
// Select first option settings
<option selected disabled hidden style='display: none' value=''></option>
// 將 JSON 資料轉成 CSV
var json = json3.items
var fields = Object.keys(json[0])
var replacer = function(key, value) { return value === null ? '' : value }
var csv = json.map(function(row){
return fields.map(function(fieldName){
return JSON.stringify(row[fieldName], replacer)
}).join(',')
})
csv.unshift(fields.join(',')) // add header column
console.log(csv.join('\r\n'))
// How to add SELECT options
sel.appendChild(op);
sel.options.add(op);
sel.options[ sel.options.length ] = op;
// 驗證西洋日期的 pattern,可驗證大小月及二月是否為閏月
pattern="(?:19|20)(?:(?:[13579][26]|[02468][048])-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))|(?:[0-9]{2}-(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:29|30))|(?:(?:0[13578]|1[02])-31)))">
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment