---
# System prepended metadata

title: 數值%數顯示計算
tags: [javascript]

---

# 數值%數顯示計算
###### tags: `javascript`
❓ 狀況: 
有六個比例%數要顯示，但如果數值來源是奇數，那麼六個%數總會有六個%數加起來不會是100%的狀況。

💡 想法:
計算比例後，為了顯示看起來正常，會在計算完比例後，檢查加總後是否為100%，若沒有會將最大比例分配給最小比例1%，讓整體加總成為100%。

```typescript=
/**
 * 為顯示做數值的比例計算。
 *
 * 當計算比例時，出現 0% 以上 1% 以下的比例時，會自動上升為 1%，主要是處理直覺觀感的問題，只有 0 才會被計算成 0%
 *
 * 比例計算只要沒有小數點，無論如何都可能會出現湊不滿 100% 的情況
 * 所以計算過程會犧牲掉比例值最大的對象，扣除他的 1% 填補在其他對象上
 *
 * @param numbers 要做比例計算的數值陣列
 * @returns 表示比例的整數陣列，數值從 0~100 的整數
 */
export function getRatiosForDisplay ( numbers: number[] ) {
    /** 計算總計分 */
    const total = numbers.reduce( ( p, v ) => p + v, 0 )

    /** 生成原始比例值對象陣列，會轉換成 0 ~ 100 的數值，但保留小數點。做成物件型態方便改變數值，但不影響順序 */
    const rawRatios = numbers.map( ( v ) => ( { value: ( v / total ) * 100 } ) )

    /** 過濾出比率值大於 0% ，低於 1% 的對象。隨後稱為 "不滿對象"，其餘稱為 "非不滿對象" */
    const LessThan1P = rawRatios.filter( ( { value } ) => value && value < 1 )

    /** 被處理過的 "不滿對象" 計數 */
    let gainCount = 0

    /** 所有 "不滿對象" 被加到 1% 時，被增加的總比例 */
    const gains = LessThan1P.reduce( ( p, v ) => {
        if ( v.value < 1 ) {
            // 當判別為 "不滿對象" 時，將他們拉升到 1% ，並把增益出來的數值保存起來，並增加計數
            const gain = 1 - v.value
            v.value = 1
            gainCount++
            return p + gain
        }

        // 為 "非不滿對象" 的話則不處理，回傳保存的數值
        return p
    }, 0 )

    /** 以 "非不滿對象" 的數量，平均化 gains，隨後用來扣除使用 */
    const averageGain = gains / ( numbers.length - gainCount )

    /** 最大比率的對象。預設值為所有比率值的第一位，用於比對 */
    let maxRatioTgt = rawRatios[ 0 ]
    /** 用於最大比率對象的最終值，用所有其餘比率扣除，最終值將成為最大比率對象的值 */
    let maxRatioFinal = 100

    // 最終處理，將所有比率進行四捨五入，得出整數
    for ( const ratio of rawRatios ) {
        // "不滿對象" 已經被整數化成 1 ，所以只處理 "非不滿對象"，數值一定大於不等於 1
        if ( ratio.value > 1 ) {
            /*
            "非不滿對象" 數值整數化，但要扣除 "不滿對象" 的平均增益
            (就是拿 "非不滿對象" 的比率來填 "不滿對象" 的增益值)
             */
            ratio.value = Math.round( ratio.value - averageGain )
        }
        // 扣除掉最大比率的最終數值
        maxRatioFinal -= ratio.value

        // 找尋最大比率的對象
        if ( ratio.value > maxRatioTgt.value )maxRatioTgt = ratio
    }

    // 由於以上迴圈一定會扣到最大比率對象的數值，使 maxRatioFinal 變成負數，所以最後還得加回來，才會是正確的數值
    maxRatioTgt.value = maxRatioFinal + maxRatioTgt.value

    const resule = rawRatios.map( ( { value } ) => value )

    return resule
}

```