JS-處理key-value結構資料

Object.keys(), Object.values(), & Object.entries()

這篇的起因是 AC 2–1 期末的技術驗收題目中,其中一題是需要撰寫一段程式,將 1~3999 的正整數,轉換成羅馬字。這一題要自己設計資料結構。

問題

由於羅馬數字有7個,即 Ⅰ(1)、Ⅴ(5)、Ⅹ(10)、Ⅼ(50)、Ⅽ(100)、Ⅾ(500)和 Ⅿ(1000),但是遇到 4, 9 要把符號放左邊(右加左減),覺得這樣一下左一下右有點複雜,所以把它放在資料裡。所以這題有以下需求:(1) 資料結構如下圖,(2) 演算法要從數字大到數字小依序處理:

Image

羅馬數字的規則參考


思考

在課程中比較多使用 array 來進行迴圈迭代,所以第一次交的版本,是把羅馬文字跟十進位數字照順序放在兩個 array 裡面,這樣 loop 的時候同一個 index 可以對應到羅馬字與數字。(寫的時候還不會用 forEach )

1
2
3
4
5
6
7
8
9
10
11
12
13
function toRoman(inputNum) {
let romanNum = ''
let remainder = Number(inputNum)
const romanArray = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I']
const decimalArray = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
for (let i = 0; i < romanArray.length; i++) {
while (remainder >= decimalArray[i]) {
let count = Math.floor(remainder / decimalArray[i])
romanNum = romanNum + romanArray[i].repeat(count)
remainder = remainder % decimalArray[i]
}
}
return romanNum

因為覺得放兩個 array 還要仔細看兩個 array 有沒有缺漏造成對應錯誤,還是用 key-value 結構比較直覺。助教提示我:其實 object 也可以放到 array 裡啊~所以我就來試試(這時候已經會用 forEach 跟 arrow function 了!)。

這個資料結構是 array 裡放 object,照順序取出 object,用 Object.keys()Object.values() 取物件裡的東西。由於每個 object 裡都只有一個 key-value pair,所以[]裡的 index 值都是 0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function toRoman(inputNum) {
let romanNum = ''
let remainder = Number(inputNum)
const numPairs = [
{'M': 1000}, {'CM': 900}, {'D': 500}, {'CD': 400},
{'C': 100}, {'XC': 90}, {'L': 50}, {'XL': 40},
{'X': 10}, {'IX': 9}, {'V': 5}, {'IV': 4}, {'I': 1}
]

numPairs.forEach(pairObj => {
while (remainder >= Object.values(pairObj)[0]) {
let count = Math.floor(remainder / Object.values(pairObj)[0])
romanNum = romanNum + Object.keys(pairObj)[0].repeat(count)
remainder = remainder % Object.values(pairObj)[0]
}
})
return romanNum
}

繼續嘗試

真的不能用單純 object,然後叫他照順序來嗎!?我繼續來試~

Object.entries() 這個方法會將 object 回傳成雙層 array,再搭配 for..of 迴圈(跟之前 Python 裡面寫 for k, v in object: 一樣的功能)

Image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function toRoman(inputNum) {
let romanNum = ''
let remainder = Number(inputNum)
const numPairs = {
'M': 1000, 'CM': 900, 'D': 500, 'CD': 400,
'C': 100, 'XC': 90, 'L': 50, 'XL': 40,
'X': 10, 'IX': 9, 'V': 5, 'IV': 4, 'I': 1,
}

for (const [r, a] of Object.entries(numPairs)) {
while (remainder >= a) {
let count = Math.floor(remainder / a)
romanNum = romanNum + r.repeat(count)
remainder = remainder % a
}
}
return romanNum
}

結論

條條大路通羅馬(文字)XD

後記

寫的時候在想,現在是我先把羅馬字與數字的對應從大到小放進去了,如果到時候 object 進來是從小到大,甚至是亂排,應該有方法是可以 sorting 先把 value 照大小排序的(覺得後面應該會學到)。