Open14

単価計算アプリを Svelte で作ってみる

mkpolimkpoli

言い忘れたけど、削除ボタンを綺麗にしたうえで hover と focus 状態の style を追加した

そして sanitize.css を使うようになった(なんか便利)

まだ調節が必要だけど

mkpolimkpoli

数式も使えるようになった これなら毎 e グラム当たり π 円の商品も計算できるようになりました

mkpolimkpoli


これでやっと念願の比較ができました!商品 C が一番安いことがわかりました。

mkpolimkpoli

軽く結果ランキングをポール風にしてみた

mkpolimkpoli

このように一番高いやつと一番安いやつにアイコンをつけた……なんか結構 opinionated な感じ(お得なのが一番良くて、一番高いのはよくないみたいな)になってしまったけど、俗ではあるけど一消費者としてはやはり自然にそういう感じてしまうからな

index を min、max の計算の後知りたいから、こんなくどいアルゴリズムにしたけど、まあちゃんと動いてるし……

  function getMinMax(items: Item[]) {
    return items.reduce((acc: MinMax, ele: Item, ind: number) => {
      const price = calculatePrice(ele.value, ele.amount, NaN)

      if (isNaN(price)) {
        return acc
      }
      
      if (price > acc.max.value) {
        acc.max = {
          index: ind,
          value: price
        }
      }

      if (price < acc.min.value) {
        acc.min = {
          index: ind,
          value: price
        }
      }

      return acc
    }, {
      min: {
        index: -1,
        value: Infinity
      },
      max: {
        index: -1,
        value: -Infinity
      }
    })
  }
mkpolimkpoli

最大値と最小値を計算するアルゴリズムをもっとロバストにした

  // 基本的に初期値が [] の default map
  class PriceMap extends Map<number, number[]> {
    get(k: number): number[] {
      if (this.has(k)) {
        return super.get(k)
      } else {
        const v = []
        this.set(k, v)
        return v
      }
    }
  }
  
  // 価格から index の 1-to-n 関係(全射)
  function createPriceMap(prices: number[]): PriceMap {
    return prices.reduce((map: PriceMap, price: number, index: number) => {
      map.get(price).push(index)
      return map
    }, new PriceMap())
  }
  
  interface MinMax {
    min?: {
      value: number,
      indices: number[],
    },
    max?: {
      value: number,
      indices: number[],
    }
  }

  function createMinMax(priceMap: PriceMap): MinMax {
    const sorted = [...priceMap.entries()].filter(([value,]) => !isNaN(value)).sort(([valA,], [valB,]) => {
      return valA - valB
    })

    if (sorted.length <= 1) {
      return {}
    }

    const [valueMin, indexMin] = sorted[0]
    const [valueMax, indexMax] = sorted[sorted.length - 1]

    return {
      min: {
        value: valueMin,
        indices: indexMin,
      },
      max: {
        value: valueMax,
        indices: indexMax,
      }
    }
  }

  $: prices = items.map(({ value, amount }) => calculatePrice(value, amount))
  $: priceMap = createPriceMap(prices)
  $: minmax = createMinMax(priceMap)