ALPHA Camp 2-1 S1作業:技術筆記

2021/4/21 更新: 經同學提醒replace()只會處理目標字串中的「第一組」目標,亦即:當我要處理的字串中包含重複的字母時,使用replace()來處理字串可能會導致非預期結果(因為replace()只會處理「符合目標的第一個字母」),故追加新解法。

總結

記錄 2021 年 ALPHA Camp 學期 2-1(四月班)第一週課程「JavaScript 核心觀念」作業中有使用到的技術與相關筆記。

使用到的methods一覽:

A1 部分作業

Q1:產生隨機英數字串組

要求:

解題邏輯:

解法(直接包裝為函式):

function generateTicket () {
  const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  const ticketLetters = `${letters[Math.floor(Math.random() * 26)]}${letters[Math.floor(Math.random() * 26)]}`
  const ticketNum = Math.floor(Math.random() * 10000).toString().padStart(4, '0')
  return `${ticketLetters}${ticketNum}` // 回傳「兩個大寫英文字母+四位數字組合」的彩券號碼
}

筆記:

Q2:加密姓名

要求:

// input
let name = 'Bernard'

// output should be 'Be*****'
console.log(name)

解題邏輯:

2021/4/21 更新: 以下「舊版解法」不盡然正確,因為replace()只會處理「第一個」符合目標的字母。 如果name中有重複的單字,那麼「其餘的」字母都不會被處理到;在這裡使用replace()來進行字串操作並不是最佳解。

舊版解法:

for (const n in name) {
  if (n > 1) name = name.replace(name.charAt(n),'*')
}
console.log(name) // Be*****

舊版解法包裝為函式:

function encodeName (name, replaceTo, startIndex = 2, endIndex = name.length) {
  for (const n in name) {
    if (n >= startIndex && n < endIndex) name = name.replace(name.charAt(n), replaceTo)
  }
  return name
}

console.log(encodeName('Bernard', '*')) // Be*****

筆記:

2021/4/21 追加新解法(直接包裝為函示)

function encodeName (name, replaceTo = '*', startIndex = 0, endIndex = 2) {
  const partialName = name.substring(startIndex, endIndex)
  const replace = replaceTo.repeat(name.length - endIndex)
  return `${partialName}${replace}`
}

name = encodeName(name)
console.log(name) // Be*****

解析:

Q3:加密信箱

要求:

// input
let email = 'bernard@example.com'

// output should be 'ber...@example.com'
console.log(email)

解題邏輯:

解法:

let namePartial = email.substring(0, email.indexOf('@') / 2)
email = `${namePartial}...${email.substring(email.indexOf('@'), email.length )}`

console.log(email) // ber...@example.co

包裝為函式:

function getEmailName (email) {
  return email.substring(0, email.indexOf('@'))
}

function getEmailDomain (email) {
  return email.substring(email.indexOf('@'), email.length)
}

function encodeNameForEmail (name, replaceTo = '...', keepStartIndex = 0, keepEndIndex = name.length / 2) {
  return `${name.substring(keepStartIndex, keepEndIndex)}${replaceTo}`
}

function encodeEmail (email) {
  const name = getEmailName(email)
  const encodeName = encodeNameForEmail(name)
  const domain = getEmailDomain(email)
  return `${encodeName}${domain}`
}

console.log(encodeEmail('bernard@example.com'))  // ber...@example.com
console.log(encodeEmail('info@example.com'))     // in...@example.com
console.log(encodeEmail('genie@example.com'))    // ge...@example.com
console.log(encodeEmail('eva_chan@example.com')) // eva_...@example.com

筆記:

A2 部分作業

參加者與黑名單皆為單純陣列

第一版解法:兩層迴圈會讓時間複雜度升高,還有優化空間

for (let i = 0; i < blackList.length; i ++) {
  for (let j = 0; j < players.length; j ++) {
    if (blackList[i] === players[j]) {
      players.splice(j, 1)
    }
  }
}

觀摩後改良:從參加者陣列的尾部開始遍歷並移除黑名單對象,就不用擔心splice()影響陣列長度,且只需遍歷參加者陣列、不用遍歷黑名單

// 直接包裝為函式
// 此函式會直接修改players的內容,而不會回傳一個新陣列
function playersRemoveBlackListArray (players, blackList) {
  for (let i = players.length - 1; i >= 0; i --) {
    if (blackList.includes(players[i])) players.splice(i, 1)
  }
}

參加者與黑名單皆為包含物件的陣列

第一版解法:雙迴圈處理(邏輯與單純陣列的第一版解法一致,此處不再重複列出) 觀摩到的解法:使用filter()過濾掉位在黑名單中的名字

const playersAllowed = players.filter(player => player.name !== 'Tim' && player.name !== 'Walter')
console.log(playersAllowed)

但以上的程式碼是直接上黑名單手動輸入到filter()中,日後黑名單若擴充還需更新程式碼內容,彈性不夠好。 於是改良第三版:

function getPermittedUsers (users, blackList) {
  const black = getBlackListNames(blackList)
  const pass = users.filter(user => black.indexOf(user.name) === -1)
  return pass
}

function getBlackListNames (blackList) {
  const names = []
  blackList.forEach(black => names.push(black.name))
  return names // 會得到 ['Tim', 'Walter']
}

const permittedUsers = getPermittedUsers(players, blackList)
console.log(permittedUsers)
/*
輸出結果如下:
[
  { name: 'Bernard', email: 'bernard@example.com', ticket: 'XL3558' },
  { name: 'Youchi', email: 'youchi@example.com', ticket: 'AH9188' },
  { name: 'Yenting', email: 'yenting@example.com', ticket: 'LO9903' },
  { name: 'Angela', email: 'angela@example.com', ticket: 'HY7212' },
  { name: 'Yvonne', email: 'yvonne@example.com', ticket: 'CH7684' },
  { name: 'Ellen', email: 'ellen@example.com', ticket: 'BB1750' },
  { name: 'Kevin', email: 'kevin@example.com', ticket: 'TT1804' },
  { name: 'Russell', email: 'russell@example.com', ticket: 'SI0305' }
]
*/

A5 部分作業

第一版 drawWinner()

第二版 drawWinner()

swap()

announceMsg()

將得獎者相關資料輸出到 console 上,此為題目預設的內容,不更動

encodeName()

為配合announceMsg(),修改replaceTo將其內容預設為*

getEmailName()、getEmailDomain()、encodeNameForEmail()、encodeEmail()、generateTicket()

解題思考流程請參考本篇文章上半部

assignTicket()

分配參加獎

bonus track:時間複雜度(Time complexity)

以下筆記參考中文維基百科與 CS50 2018 Lecture 0:

「時間複雜度」:指的是某一演算法的執行時間

參考資料