Last active
July 4, 2017 07:31
-
-
Save twlca/80bfa963c016689888d43626b907faa3 to your computer and use it in GitHub Desktop.
// 將資料依指定的 property 組成群組
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 要求: | |
// 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