Elixir Weekly Challenge: Magic Number

The first of this week’s PWC 268 tasks is called Magic Number.

Task 1: Magic Number

You are given two arrays of integers of same size, @x and @y.

Write a script to find the magic number that when added to each elements of one of the array gives the second array. Elements order is not important.

Example 1

Input: @x = (3, 7, 5)
       @y = (9, 5, 7)
Output: 2

The magic number is 2.
@x = (3, 7, 5)
   +  2  2  2
@y = (5, 9, 7)

Example 2

Input: @x = (1, 2, 1)
       @y = (5, 4, 4)
Output: 3

The magic number is 3.
@x = (1, 2, 1)
   +  3  3  3
@y = (5, 4, 4)

Example 3

Input: @x = (2)
       @y = (5)
Output: 3

In my Raku / Perl / Python implementations of this task, I sorted the two lists, then found the difference between the first elements on both lists, then confirmed that the difference was the same for each pair of elements in the two lists.

Writing this up functionally wasn’t that hard… but it took me a while to get past how I was going to do the last part:

  def magicNumber(x, y) do
    xS = Enum.sort(x)
    yS = Enum.sort(y)
    magic = Enum.at(yS, 0) - Enum.at(xS, 0)
    # there be dragons here
  end

I thought about looping over both lists xS and yS (x-sorted and y-sorted) in some kind of double-indexed loop:

for i <- xS, j <- yS do
  # there be dragons here
end

But my big concern was being able to return the information about whether or not the magic number applied to each number pair across xS and yS. Then I remembered the pattern-matching I did with task 2 last week where I defined a function two ways: once to pull an element off a list passed in as a parameter, and another time to handle when the list was exhausted. I could do the same here: the “pull the first element off the list” version could pull the first element off both lists and make sure that their difference matched the magic number we’ve already established (and, if not, returning an error condition) and the “empty list” version could return the success condition saying everything matched. This would leave me with this:

  defp checkNums(magic, [], []) do
    { :ok, magic }
  end

  defp checkNums(magic, [x | rX], [y | rY]) do
    if y - x == magic do
      checkNums(magic, rX, rY)
    else
      { :err, 0 }
    end
  end

  def magicNumber(x, y) do
    xS = Enum.sort(x)
    yS = Enum.sort(y)
    magic = Enum.at(yS, 0) - Enum.at(xS, 0)
    checkNums(magic, xS, yS)
  end

In my code that was calling magicNumber(x, y), I wanted to be able to check to see if we succeeded in finding a “magic number” by checking if we returned an atom :ok. So I wound up with my entire solution looking like this:

#!/usr/bin/env elixir

defmodule PWC do
  defp checkNums(magic, [], []) do
    { :ok, magic }
  end

  defp checkNums(magic, [x | rX], [y | rY]) do
    if y - x == magic do
      checkNums(magic, rX, rY)
    else
      { :err, 0 }
    end
  end

  def magicNumber(x, y) do
    xS = Enum.sort(x)
    yS = Enum.sort(y)
    magic = Enum.at(yS, 0) - Enum.at(xS, 0)
    checkNums(magic, xS, yS)
  end

  def solution(x, y) do
    IO.puts("Input: @x = (" <> Enum.join(x, ", ") <> ")")
    IO.puts("       @y = (" <> Enum.join(y, ", ") <> ")")
    case PWC.magicNumber(x, y) do
      {:ok, magic} ->
        IO.puts("Output: #{to_string(magic)}")
        IO.puts("\nThe magic number is #{to_string(magic)}.")
        IO.puts("@x = (" <> Enum.join(x, ", ") <> ")")
        IO.puts("   +  " <> Enum.join(
          Enum.map(x, fn _ -> magic end), "  "
        ))
        IO.puts("@y = (" <> Enum.join(
          Enum.map(x, fn n -> n + magic end), ", "
        ) <> ")")
      {:err, _} ->
        IO.puts('Output: no magic number')
    end
  end
end

IO.puts("Example 1:")
PWC.solution([3, 7, 5], [9, 5, 7])

IO.puts("\nExample 2:")
PWC.solution([1, 2, 1], [5, 4, 4])

IO.puts("\nExample 3:")
PWC.solution([2], [5])

IO.puts("\nExample 4:")
PWC.solution([1, 2], [4, 2])

$ elixir/ch-1.exs
Example 1:
Input: @x = (3, 7, 5)
       @y = (9, 5, 7)
Output: 2

The magic number is 2.
@x = (3, 7, 5)
   +  2  2  2
@y = (5, 9, 7)

Example 2:
Input: @x = (1, 2, 1)
       @y = (5, 4, 4)
Output: 3

The magic number is 3.
@x = (1, 2, 1)
   +  3  3  3
@y = (4, 5, 4)

Example 3:
Input: @x = (2)
       @y = (5)
Output: 3

The magic number is 3.
@x = (2)
   +  3
@y = (5)

Example 4:
Input: @x = (1, 2)
       @y = (4, 2)
Output: no magic number

The code can be found on GitHub at https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-268/packy-anderson/elixir