Beginner - How to Convert Values in a struct

Good afternoon,

I’m starting to play with Elixir and currently working my way through the Pragmatic Studio course while doing little experimentation on the side. I’m finding a whole lot to be confusing but I’m sure if I persist something will eventually click.

Anyway, like help or a pointer on structuring a module and functions. I’ve got the following module with a struct which I would like to convert between imperial and metric. How would I iterate though the measures to perform the conversion calculations, should each be measure have it’s own function for conversion which are piped together?

  defstruct(
        measure: :metric,
        location: :uk,
        measures: %{
              length: nil,
              height: nil,
              depth: nil,
              weight: nil,
        },
  )
  
  def convert_to_imperial( %Details{ measure: :metric } = details ) do
        # Loop through measures - 
        # perform cm_to_inch for sizes
        # kg_to_lbs for weight
        # measure: to :imperial
  end
  def convert_to_imperial( %Details{} ), do: IO.puts("Not metric")
  
  defp cm_to_inch(cm), do: Dec.mult(Dec.new(cm), Dec.new(0.3937)) |> Dec.to_float()
  defp kg_to_lbs(kg),  do: Dec.mult(Dec.new(kg), Dec.new(2.2046)) |> Dec.to_float()

iex:  a = Item.Detail%{ measure: metric, measures: %{ length: 44, height: 44, depth: 10, weight: 55 } }
iex:  Item.Detail.convert_to_imperial(a)

You can iterate on a map’s key/value pairs using for, for example:

  def convert_to_imperial(%Details{measure: :metric} = details) do
    measures =
      for {dimension, value} <- details.measures, into: %{} do
        {dimension, metric_to_imperial(dimension, value)}
      end
    %Details{details | measures: measures}
  end

  defp metric_to_imperial(dimension, cm) when dimension in [:length, :height, :depth] do
    Dec.mult(Dec.new(cm), Dec.new(0.3937)) |> Dec.to_float()
  end

  defp metric_to_imperial(:weight, kg) do
    Dec.mult(Dec.new(kg), Dec.new(2.2046)) |> Dec.to_float()
  end

Although here since there aren’t many fields you can just build the map directly too:

  def convert_to_imperial(%Details{measure: :metric} = details) do
    metric = details.measures
    imperial = %{
      length: cm_to_inch(metric.length),
      height: cm_to_inch(metric.height),
      # etc.
    }
    %Details{details | measures: imperial}
  end
1 Like